----- ./scripts/start_tpm2.sh -----
#!/bin/bash
PORT=2322
if [ -n "$GITHUB_WORKFLOW" ]; then
TPM2_STATE_DIR=/tmp/swtpm/tpm2
else
TPM2_STATE_DIR=/usr/share/swtpm/tpm2
fi
mkdir -p "$TPM2_STATE_DIR"
echo "starting tpm"
sudo swtpm chardev \
--vtpm-proxy \
--tpmstate dir="$TPM2_STATE_DIR" \
--tpm2 \
--log level=20 \
--ctrl type=tcp,port="${PORT}"----- END ./scripts/start_tpm2.sh -----
----- ./scripts/start_tpm1.sh -----
#!/bin/bash
PORT=2321
if [ -n "$GITHUB_WORKFLOW" ]; then
TPM1_STATE_DIR=/tmp/swtpm/tpm1
else
TPM1_STATE_DIR=/usr/share/swtpm/tpm1
fi
mkdir -p "$TPM1_STATE_DIR"
if command -v swtpm_setup >/dev/null 2>&1; then
SWTPM_SETUP_CMD=swtpm_setup
elif command -v swtpm-setup >/dev/null 2>&1; then
SWTPM_SETUP_CMD=swtpm-setup
else
echo "swtpm setup tool not found (expected swtpm_setup or swtpm-setup)" >&2
exit 1
fi
# do we need to init tpm? (only needed on tpm 1.2)
if [ ! -f "$TPM1_STATE_DIR/tpm-00.permall" ]; then
echo "initializing tpm"
sudo "$SWTPM_SETUP_CMD" \
--tpmstate "$TPM1_STATE_DIR" \
--createek \
--create-platform-cert
if [ $? -ne 0 ]; then
echo "swtpm_setup failed. Aborting." >&2
exit 1
fi
echo "setup done"
else
echo "tpm already initialized"
fi
echo "starting tpm"
sudo swtpm chardev \
--vtpm-proxy \
--tpmstate dir="$TPM1_STATE_DIR" \
--log level=20 \
--locality allow-set-locality \
--ctrl type=tcp,port="${PORT}"
----- END ./scripts/start_tpm1.sh -----
----- ./scripts/build_vtpm.sh -----
#!/bin/bash
set -euo pipefail
KERNEL_TAG=$(uname -r | sed 's/\([0-9]*\.[0-9]*\).*/v\1/')
BASE_URL="https://raw.githubusercontent.com/torvalds/linux/${KERNEL_TAG}/drivers/char/tpm"
BUILD_DIR=$(mktemp -d)
file_list=(
"tpm_vtpm_proxy.c"
"tpm.h"
)
for file in "${file_list[@]}"; do
wget "${BASE_URL}/${file}" -O "${BUILD_DIR}/${file}"
done
cat <<EOF > "${BUILD_DIR}/Makefile"
obj-m += tpm_vtpm_proxy.o
all:
make -C /lib/modules/\$(shell uname -r)/build M=${BUILD_DIR} modules
clean:
make -C /lib/modules/\$(shell uname -r)/build M=${BUILD_DIR} clean
EOF
make -C "${BUILD_DIR}"
if [ -f "${BUILD_DIR}/tpm_vtpm_proxy.ko" ]; then
insmod "${BUILD_DIR}/tpm_vtpm_proxy.ko"
else
echo "failed to build tpm_vtpm_proxy"
exit 1
fi----- END ./scripts/build_vtpm.sh -----
----- ./examples/tlcl_kernver.rs -----
#![allow(unused_variables)]
#[cfg(feature = "tpm1_2")]
use libcros::tlcl::tpm12::constants::{TPM_NV_PER_PPREAD, TPM_NV_PER_PPWRITE};
use libcros::{
LOG, LOG_FATAL, Logger, kv_set,
libargs::ArgCheck,
tlcl::{TlclDefineSpace, TlclRead, TlclUndefineSpace, TlclWrite},
};
const NV_INDEX: u32 = 0x1008;
/* kernver 0x00000000 */
const KERN_VER: &[u8] = &[
0x02, 0x4c, 0x57, 0x52, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8,
];
fn main() {
let mut args: ArgCheck = ArgCheck::new();
let verbose: bool = args.fbool("--verbose", "", "Enable debug messages");
let flags_tpm_path = args.fequals_str(
"--tpm-path",
"-t",
"Specify a custom TPM device to use in /dev/tpmX format",
);
args.check_help();
if flags_tpm_path.is_empty() {
kv_set(libcros::keys::TPM_PATH, "/dev/tpm0");
} else {
kv_set(libcros::keys::TPM_PATH, &*flags_tpm_path);
}
Logger::init(verbose, true);
/* platform hierarchy on TPM 1.2 requires physical presence */
#[cfg(feature = "tpm1_2")]
{
use libcros::tlcl::{TlclAssertPhysicalPresence, TlclPhysicalPresenceCMDEnable, TlclSetEnable};
let rc = TlclPhysicalPresenceCMDEnable();
if rc != 0 {
LOG_FATAL!("PhysicalPresenceCMDEnable failed: 0x{:X}", rc);
}
let rc = TlclAssertPhysicalPresence();
if rc != 0 {
LOG_FATAL!("AssertPhysicalPresence failed: 0x{:X}", rc);
}
let rc = TlclSetEnable();
if rc != 0 {
LOG_FATAL!("TlclSetEnable failed: 0x{:X}", rc);
}
}
/* tpm2_nvdefine --hierarchy=p --size=13 0x1008
PPWRITE|PPREAD on tpm 1.2, those two plus PLATFORMCREATE on tpm 2.0 */
#[cfg(feature = "tpm2_0")]
let perm = libcros::tlcl::tpm20::constants::TPMA_NV_PLATFORMCREATE
| libcros::tlcl::tpm20::constants::TPMA_NV_PPWRITE
| libcros::tlcl::tpm20::constants::TPMA_NV_PPREAD;
#[cfg(feature = "tpm1_2")]
let perm = TPM_NV_PER_PPWRITE | TPM_NV_PER_PPREAD;
LOG!("undefining NV index 0x{:X}", NV_INDEX);
TlclUndefineSpace(NV_INDEX);
LOG!(
"defining NV index 0x{:X} with size {} and perm 0x{:X}",
NV_INDEX,
KERN_VER.len(),
perm
);
let rc = TlclDefineSpace(NV_INDEX, perm, KERN_VER.len() as u32);
if rc != 0 {
LOG_FATAL!(rc.try_into().unwrap(); "TlclDefineSpace failed with error code: {:x}", rc);
}
/* tpm2_nvwrite --hierarchy=p --input=kernver 0x1008 */
LOG!("writing {} bytes to 0x{:X}", KERN_VER.len(), NV_INDEX);
let rc = TlclWrite(
NV_INDEX,
KERN_VER.as_ptr() as *const core::ffi::c_void,
KERN_VER.len() as u32,
);
if rc != 0 {
LOG_FATAL!(rc.try_into().unwrap(); "TlclWrite failed with error code: {:x}", rc);
}
let mut outbuf = [0u8; KERN_VER.len()];
let rc = TlclRead(
NV_INDEX,
outbuf.as_mut_ptr() as *mut core::ffi::c_void,
KERN_VER.len() as u32,
);
if rc != 0 {
LOG_FATAL!(rc.try_into().unwrap(); "TlclRead failed with error code: {:x}", rc);
}
let hex_string: String = outbuf.iter().map(|b| format!("{:02x} ", b)).collect();
LOG!("kernver (hex): {}", hex_string);
}
----- END ./examples/tlcl_kernver.rs -----
----- ./examples/key_value.rs -----
use libcros::{LOG, LOG_DBG, key_types, keys, kv_get, kv_set, libargs::ArgCheck};
fn main() {
let mut args: ArgCheck = ArgCheck::new();
let _verbose: bool = args.fbool("--verbose", "", "Enable debug messages");
let mut value = args.fequals_str("--value", "-f", "Value of the key");
args.check_help();
if value.is_empty() {
value = "Hello, World!".to_string();
}
LOG!(
"value of {}: {:?}",
keys::EXAMPLE,
kv_get(key_types::STRING, keys::EXAMPLE)
);
LOG_DBG!("setting {} to {}", keys::EXAMPLE, value);
kv_set(keys::EXAMPLE, value);
LOG!(
"value of {}: {:?}",
keys::EXAMPLE,
kv_get(key_types::STRING, keys::EXAMPLE)
);
}
----- END ./examples/key_value.rs -----
----- ./examples/tlcl_read.rs -----
use libcros::{LOG, LOG_FATAL, Logger, kv_get, kv_set, libargs::ArgCheck, tlcl::TlclRead};
const NV_INDEX: u32 = 0x1008;
const SIZE: usize = 0xD;
fn main() {
let mut args: ArgCheck = ArgCheck::new();
let verbose: bool = args.fbool("--verbose", "", "Enable debug messages");
let flags_tpm_path = args.fequals_str(
"--tpm-path",
"-t",
"Specify a custom TPM device to use in /dev/tpmX format",
);
args.check_help();
if flags_tpm_path.is_empty() {
kv_set(libcros::keys::TPM_PATH, "/dev/tpm0");
} else {
kv_set(libcros::keys::TPM_PATH, &*flags_tpm_path);
}
Logger::init(verbose, true);
let tpm = kv_get(libcros::key_types::STRING, libcros::keys::TPM_PATH);
LOG!(
"reading {} bytes from index {} on {:?}",
SIZE,
NV_INDEX,
tpm
);
let mut outbuf = [0u8; SIZE];
let rc = TlclRead(
NV_INDEX,
outbuf.as_mut_ptr() as *mut core::ffi::c_void,
SIZE.try_into().unwrap(),
);
if rc != 0 {
LOG_FATAL!(rc.try_into().unwrap(); "TlclRead failed with error code: {:x}", rc);
}
LOG!("read output: {:02X?}", outbuf);
}
----- END ./examples/tlcl_read.rs -----
----- ./examples/tlcl_save_state.rs -----
use libcros::{LOG, LOG_FATAL, Logger, kv_get, kv_set, libargs::ArgCheck, tlcl::TlclSaveState};
fn main() {
let mut args: ArgCheck = ArgCheck::new();
let verbose: bool = args.fbool("--verbose", "", "Enable debug messages");
let flags_tpm_path = args.fequals_str(
"--tpm-path",
"-t",
"Specify a custom TPM device to use in /dev/tpmX format",
);
args.check_help();
if flags_tpm_path.is_empty() {
kv_set(libcros::keys::TPM_PATH, "/dev/tpm0");
} else {
kv_set(libcros::keys::TPM_PATH, &*flags_tpm_path);
}
Logger::init(verbose, true);
let tpm = kv_get(libcros::key_types::STRING, libcros::keys::TPM_PATH);
LOG!("save state on {:?}", tpm);
let rc = TlclSaveState();
if rc != 0 {
LOG_FATAL!(rc.try_into().unwrap(); "TlclSaveState failed with error code: {:x}", rc);
}
}
----- END ./examples/tlcl_save_state.rs -----
----- ./examples/tlcl_startup.rs -----
#[cfg(feature = "tpm1_2")]
use libcros::tlcl::constants::TPM_E_INVALID_POSTINIT;
#[cfg(feature = "tpm2_0")]
use libcros::tlcl::tpm20::types::TPM_RC_INITIALIZE;
use libcros::{LOG, LOG_FATAL, Logger, kv_get, kv_set, libargs::ArgCheck, tlcl::TlclStartup};
fn main() {
let mut args: ArgCheck = ArgCheck::new();
let verbose: bool = args.fbool("--verbose", "", "Enable debug messages");
let flags_tpm_path = args.fequals_str(
"--tpm-path",
"-t",
"Specify a custom TPM device to use in /dev/tpmX format",
);
args.check_help();
if flags_tpm_path.is_empty() {
kv_set(libcros::keys::TPM_PATH, "/dev/tpm0");
} else {
kv_set(libcros::keys::TPM_PATH, &*flags_tpm_path);
}
Logger::init(verbose, true);
let tpm = kv_get(libcros::key_types::STRING, libcros::keys::TPM_PATH);
LOG!("startup on {:?}", tpm);
let rc = TlclStartup();
#[cfg(feature = "tpm2_0")]
if rc == TPM_RC_INITIALIZE {
LOG!("startup skipped: already initialized (rc={:x})", rc);
return;
}
#[cfg(feature = "tpm1_2")]
if rc == TPM_E_INVALID_POSTINIT {
/* "(?)" here because i'm pretty sure this is just the same as TPM_RC_INITIALIZE on tpm2, but i'm not 100% sure */
LOG!("startup skipped: already initialized(?) (rc={:x})", rc);
return;
}
if rc != 0 {
LOG_FATAL!(rc.try_into().unwrap(); "TlclStartup failed with error code: {:x}", rc);
}
}
----- END ./examples/tlcl_startup.rs -----
----- ./examples/tlcl_clear.rs -----
use libcros::{
LOG, LOG_FATAL, Logger, kv_get, kv_set,
libargs::ArgCheck,
tlcl::{
TlclAssertPhysicalPresence, TlclForceClear, TlclPhysicalPresenceCMDEnable, TlclSetEnable,
},
};
fn main() {
let mut args: ArgCheck = ArgCheck::new();
let verbose: bool = args.fbool("--verbose", "", "Enable debug messages");
let flags_tpm_path = args.fequals_str(
"--tpm-path",
"-t",
"Specify a custom TPM device to use in /dev/tpmX format",
);
args.check_help();
if flags_tpm_path.is_empty() {
kv_set(libcros::keys::TPM_PATH, "/dev/tpm0");
} else {
kv_set(libcros::keys::TPM_PATH, &*flags_tpm_path);
}
Logger::init(verbose, true);
let tpm = kv_get(libcros::key_types::STRING, libcros::keys::TPM_PATH);
LOG!("clearing {:?}", &tpm);
let rc = TlclPhysicalPresenceCMDEnable();
if rc != 0 {
LOG_FATAL!(rc.try_into().unwrap(); "TlclPhysicalPresenceCMDEnable failed with error code: {:x}", rc);
}
let rc = TlclAssertPhysicalPresence();
if rc != 0 {
LOG_FATAL!(rc.try_into().unwrap(); "TlclAssertPhysicalPresence failed with error code: {:x}", rc);
}
/* on tpm2.0, this is the only one needed, everything else
is for tpm 1.2 */
let rc = TlclForceClear();
if rc != 0 {
LOG_FATAL!(rc.try_into().unwrap(); "TlclForceClear failed with error code: {:x}", rc);
}
let rc = TlclSetEnable();
if rc != 0 {
LOG_FATAL!(rc.try_into().unwrap(); "TlclSetEnable failed with error code: {:x}", rc);
}
}
----- END ./examples/tlcl_clear.rs -----
----- ./examples/tlcl_read_with_offset.rs -----
use libcros::{
LOG, LOG_FATAL, Logger, kv_get, kv_set, libargs::ArgCheck, tlcl::TlclReadWithOffset,
};
const NV_INDEX: u32 = 0x1008;
const SIZE: usize = 0xC;
const OFFSET: u32 = 0x1;
fn main() {
let mut args: ArgCheck = ArgCheck::new();
let verbose: bool = args.fbool("--verbose", "", "Enable debug messages");
let flags_tpm_path = args.fequals_str(
"--tpm-path",
"-t",
"Specify a custom TPM device to use in /dev/tpmX format",
);
args.check_help();
if flags_tpm_path.is_empty() {
kv_set(libcros::keys::TPM_PATH, "/dev/tpm0");
} else {
kv_set(libcros::keys::TPM_PATH, &*flags_tpm_path);
}
Logger::init(verbose, true);
let tpm = kv_get(libcros::key_types::STRING, libcros::keys::TPM_PATH);
LOG!(
"reading {} bytes from index {} at offset {} on {:?}",
SIZE,
NV_INDEX,
OFFSET,
tpm
);
let mut outbuf = [0u8; SIZE];
let rc = TlclReadWithOffset(
NV_INDEX,
SIZE as u32,
OFFSET,
outbuf.as_mut_ptr() as *mut core::ffi::c_void,
);
if rc != 0 {
LOG_FATAL!(rc.try_into().unwrap(); "TlclReadWithOffset failed with error code: {:x}", rc);
}
LOG!("read output: {:02X?}", outbuf);
}
----- END ./examples/tlcl_read_with_offset.rs -----
----- ./examples/ui.rs -----
use std::io::Write;
use libcros::{
LOG,
ui::{header::ui_header, menu::selection_menu, *},
};
const SPLASH: &str = concat!(
r#"example splash here
(can be multi-line)"#
);
fn menu_handler(option: MenuOption) {
if !option.enabled {
return;
}
match option.text.as_str() {
"Foobar" => {
LOG!("selected foobar");
}
"Example" => {
LOG!("selected example");
}
"Exit" => {
std::process::exit(0);
}
_ => {
LOG!("invalid option");
}
}
utils::enter_to_continue();
}
fn main() {
let mut text = String::new();
text.push_str("Example additional text line 1\n");
text.push_str("Example additional text line 2\n");
loop {
print!("\x1b[H\x1b[2J");
std::io::stdout().flush().unwrap();
ui_header(&SPLASH, &text);
let options = vec![
MenuOption::new("Foobar", true, constants::COLOR_RESET),
MenuOption::new("Example", true, constants::COLOR_GREEN_B),
MenuOption::new("Disabled", false, constants::COLOR_RESET),
MenuOption::new("Exit", true, constants::COLOR_RESET),
];
if let Some(choice) = selection_menu(&options) {
menu_handler(options[choice].clone());
}
}
}
----- END ./examples/ui.rs -----
----- ./examples/logger.rs -----
use libcros::{LOG, LOG_DBG, LOG_FATAL, LOG_FATAL_NOEXIT, Logger, libargs::ArgCheck};
fn main() {
let mut args: ArgCheck = ArgCheck::new();
let verbose: bool = args.fbool("--verbose", "", "Enable debug messages");
let use_colors: bool = args.fbool("--colors", "-c", "Use colors when logging");
let rc = args.fequals_str("--rc", "-r", "Exit code for fatal errors");
args.check_help();
let parsed_rc: i32 = if rc.is_empty() {
-1
} else {
rc.parse().unwrap_or(-1)
};
/* arg1 to init is a boolean on whether or
not we should be enabling verbose logging.
verbose logging is done with LOG_DBG!(msg).
arg2 is a boolean on whether or not not we
should be using colors when logging. by default,
we don't use colors but this can optionally be enabled.
*/
Logger::init(verbose, use_colors);
LOG!("This is an regular log");
LOG_DBG!("This is a verbose log");
LOG_FATAL_NOEXIT!("This is a fatal log but will not automatically exit the program.");
LOG_FATAL!(parsed_rc;
"This is a fatal log and will automatically exit the program."
);
}
----- END ./examples/logger.rs -----
----- ./examples/tlcl_write.rs -----
use libcros::{LOG, LOG_FATAL, Logger, kv_get, kv_set, libargs::ArgCheck, tlcl::TlclWrite};
const NV_INDEX: u32 = 0x1008;
// kernver 0x00010002
const DATA: [u8; 13] = [
0x02, 0x4C, 0x57, 0x52, 0x47, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x33,
];
fn main() {
let mut args: ArgCheck = ArgCheck::new();
let verbose: bool = args.fbool("--verbose", "", "Enable debug messages");
let flags_tpm_path = args.fequals_str(
"--tpm-path",
"-t",
"Specify a custom TPM device to use in /dev/tpmX format",
);
args.check_help();
if flags_tpm_path.is_empty() {
kv_set(libcros::keys::TPM_PATH, "/dev/tpm0");
} else {
kv_set(libcros::keys::TPM_PATH, &*flags_tpm_path);
}
Logger::init(verbose, true);
let tpm = kv_get(libcros::key_types::STRING, libcros::keys::TPM_PATH);
LOG!(
"writing {} bytes to index {} on {:?}",
DATA.len(),
NV_INDEX,
tpm
);
let rc = TlclWrite(
NV_INDEX,
DATA.as_ptr() as *const core::ffi::c_void,
DATA.len() as u32,
);
if rc != 0 {
LOG_FATAL!(rc.try_into().unwrap(); "TlclWrite failed with error code: {:x}", rc);
}
LOG!("write succeeded");
}
----- END ./examples/tlcl_write.rs -----
----- ./examples/sysinfo.rs -----
use std::fs::File;
use libcros::{
LOG, Logger, kv_get, kv_set,
diskutils::gpt::read_header,
libargs::ArgCheck,
sysinfo::{
backend::{close_gsc_socket, open_gsc_socket},
get_firmware_management_parameters, get_firmware_rollback_version, get_gsc_board_id,
get_gsc_version, get_kernel_rollback_version, get_tpm_version,
},
};
/*
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_1: u32 = 0x100;
pub const TPM_RC_HANDLE: u32 = 0x08B;
}
}
fn main() {
let mut args: ArgCheck = ArgCheck::new();
let verbose: bool = args.fbool("--verbose", "", "Enable debug messages");
let flags_tpm_path = args.fequals_str(
"--tpm-path",
"-t",
"Specify a custom TPM device to use (/dev/tpmX format, e.g: /dev/tpm0",
);
let flags_disk_path = args.fequals_str(
"--disk",
"-d",
"Specify a disk to read the GPT header from (/dev/[device] format, e.g: /dev/sda)",
);
#[cfg(feature = "tpm2_0")]
let ph_disabled: bool = args.fbool(
"--disable-platform-hierarchy",
"-p",
"disable platform hierarchy",
);
args.check_help();
if flags_tpm_path.is_empty() {
kv_set(libcros::keys::TPM_PATH, "/dev/tpm0");
} else {
kv_set(libcros::keys::TPM_PATH, &*flags_tpm_path);
}
let disk_path = if flags_disk_path.is_empty() {
"/dev/sda".to_string()
} else {
flags_disk_path.clone()
};
#[cfg(feature = "tpm2_0")]
if ph_disabled {
kv_set(libcros::keys::PH_DISABLED, true);
} else {
kv_set(libcros::keys::PH_DISABLED, false);
}
Logger::init(verbose, true);
kv_get(libcros::key_types::STRING, libcros::keys::TPM_PATH);
let tpm_version: String = get_tpm_version();
let kernver = get_kernel_rollback_version();
let fwver = get_firmware_rollback_version();
let fwmp = get_firmware_management_parameters();
/* Before doing anything GSC-related, but after doing our regular TPM activities, we must open the GSC socket.. */
open_gsc_socket();
let version: libcros::gsc::constants::first_response_pdu = get_gsc_version();
let bid: libcros::gsc::constants::board_id = get_gsc_board_id();
let keyid_ro = u32::from_be(version.keyid[0]);
let keyid_rw = u32::from_be(version.keyid[1]);
let backup_ro = u32::from_be(version.backup_ro_offset);
let backup_rw = u32::from_be(version.backup_rw_offset);
let shv_ro = (
version.shv[0].epoch,
u32::from_be(version.shv[0].major),
u32::from_be(version.shv[0].minor),
);
let shv_rw = (
version.shv[1].epoch,
u32::from_be(version.shv[1].major),
u32::from_be(version.shv[1].minor),
);
let board_type: u32 = u32::from_be(bid.board_type);
let type_inv: u32 = u32::from_be(bid.type_inv);
let flags: u32 = u32::from_be(bid.flags);
/* Now that we're done, we can close our connection to the GSC. */
close_gsc_socket();
LOG!("TPM version: {}", tpm_version);
LOG!(
"Kernel rollback version: 0x{:08x}",
kernver.rollback_version
);
LOG!(
"Firmware rollback version: 0x{:08x}",
fwver.rollback_version
);
if fwmp.rc == tpm20::types::TPM_RC_HANDLE | tpm20::types::TPM_RC_1 {
LOG!("FWMP index doesn't exist!")
} else {
/* This is actually FWMP flags, but it's stored in rollback_version */
LOG!("FWMP: 0x{:08x}", fwmp.rollback_version);
}
LOG!("Key IDs:");
LOG!(" - RO: {:x}", keyid_ro);
LOG!(" - RW: {:x}", keyid_rw);
LOG!("Backup Offsets:");
LOG!(" - Inactive RO: {:x}", backup_ro);
LOG!(" - Inactive RW: {:x}", backup_rw);
LOG!("Versions:");
LOG!(" - RO: {}.{}.{}", shv_ro.0, shv_ro.1, shv_ro.2);
LOG!(" - RW: {}.{}.{}", shv_rw.0, shv_rw.1, shv_rw.2);
LOG!(
"Board ID: {:08x}:{:08x}:{:08x}",
board_type,
type_inv,
flags
);
/* TODO(kxtz): make this easier to do with a sysinfo function */
match File::open(&disk_path) {
Ok(mut f) => match read_header(&mut f) {
Ok(header) => {
let sig = std::str::from_utf8(&header.magic).unwrap_or("???");
let revision = header.revision;
let header_size = header.header_size;
let current_lba = header.current_lba;
let backup_lba = header.backup_lba;
let first_usable_lba = header.first_usable_lba;
let last_usable_lba = header.last_usable_lba;
let partition_entry_lba = header.partition_entry_lba;
let num_entries = header.num_partition_entries;
let entry_size = header.size_partition_entry;
let header_crc32 = header.header_crc32;
let part_crc32 = header.partition_entry_array_crc32;
LOG!("GPT Header ({}):", disk_path);
LOG!(" signature: {}", sig);
LOG!(" revision: {}.{}", revision >> 16, revision & 0xffff);
LOG!(" header size: {} bytes", header_size);
LOG!(" current lba: {}", current_lba);
LOG!(" backup lba: {}", backup_lba);
LOG!(
" usable lba range: {} - {}",
first_usable_lba,
last_usable_lba
);
LOG!(" partition table @ lba: {}", partition_entry_lba);
LOG!(" entries: {}", num_entries);
LOG!(" entry size: {}", entry_size);
LOG!(" header crc32: 0x{:08x}", header_crc32);
LOG!(" partition array crc32: 0x{:08x}", part_crc32);
}
Err(e) => {
LOG!("failed to read GPT header from {}: {}", disk_path, e);
}
},
Err(e) => {
LOG!("failed to open {}: {}", disk_path, e);
}
}
/*
TODO: we need the following:
- emmc
- cpu
- ram
- gpu(?)
- battery (incl charge, estimated time remaining, etc)
- ec info (version, chip, etc)
- wp info
- ap fw info (version, etc)
- serial number
*/
}----- END ./examples/sysinfo.rs -----
----- ./examples/crypto.rs -----
use libcros::{
LOG, LOG_DBG,
crypto::{adler32, crc32, hmac_sha256, pbkdf2_hmac_sha256, sha1, sha256},
key_types, keys,
keyval::KvValue,
kv_get, kv_set,
libargs::ArgCheck,
};
fn main() {
let mut args: ArgCheck = ArgCheck::new();
let _verbose: bool = args.fbool("--verbose", "", "Enable debug messages");
let mut value = args.fequals_str(
"--value",
"-f",
"Value to pass through to cryptography functions",
);
args.check_help();
if value.is_empty() {
value = "Hello, World!".to_string();
}
/*
Hello, World!
expected:
CRC32: 0xec4ac3d0
ADLER32: 0x1f9e046a
SHA1: 0a0a9f2a6772942557ab5355d76af442f8f65e01
SHA256: dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f
HMAC-SHA256 (key="secret"): fcfaffa7fef86515c7beb6b62d779fa4ccf092f2e61c164376054271252821ff
PBKDF2-HMAC-SHA256 (salt="saltsalt", iterations=10000, output_len=32): 39143d32b2af92c845798e0c5c3a2f60f491af611240433036bf388a5a38032a
*/
LOG_DBG!("setting value to {}", value);
kv_set(keys::EXAMPLE, value);
// always go through kv_get.
let data = match kv_get(key_types::STRING, keys::EXAMPLE) {
Some(KvValue::String(s)) => s,
_ => panic!("invalid or missing EXAMPLE value"),
};
let bytes = data.as_bytes();
LOG!("CRC32: 0x{:08x}", crc32(bytes));
LOG!("ADLER32: 0x{:08x}", adler32(bytes));
let s1 = sha1(bytes);
LOG!(
"SHA1: {}",
s1.iter().map(|b| format!("{:02x}", b)).collect::<String>()
);
let s256 = sha256(bytes);
LOG!(
"SHA256: {}",
s256
.iter()
.map(|b| format!("{:02x}", b))
.collect::<String>()
);
let hmac = hmac_sha256(b"secret", bytes);
LOG!(
"HMAC-SHA256 (key=\"secret\"): {}",
hmac
.iter()
.map(|b| format!("{:02x}", b))
.collect::<String>()
);
let pbkdf2 = pbkdf2_hmac_sha256(bytes, b"saltsalt", 10000, 32);
LOG!(
"PBKDF2-HMAC-SHA256 (salt=\"saltsalt\", iterations=10000, output_len=32): {}",
pbkdf2
.iter()
.map(|b| format!("{:02x}", b))
.collect::<String>()
);
}
----- END ./examples/crypto.rs -----
----- ./examples/libargs.rs -----
use libcros::libargs::ArgCheck;
fn main() {
let mut args: ArgCheck = ArgCheck::new();
let verbose: bool = args.fbool("--verbose", "", "Enable debug messages");
let foobar = args.fequals_str("--foobar", "-f", "Example flag");
args.check_help();
println!("Verbose logging enabled: {}", verbose);
println!("Foobar flag value: {}", foobar);
}
----- END ./examples/libargs.rs -----
----- ./examples/tlcl_resume.rs -----
#[cfg(feature = "tpm1_2")]
use libcros::tlcl::constants::TPM_E_INVALID_POSTINIT;
#[cfg(feature = "tpm2_0")]
use libcros::tlcl::tpm20::types::TPM_RC_INITIALIZE;
use libcros::{LOG, LOG_FATAL, Logger, kv_get, kv_set, libargs::ArgCheck, tlcl::TlclResume};
fn main() {
let mut args: ArgCheck = ArgCheck::new();
let verbose: bool = args.fbool("--verbose", "", "Enable debug messages");
let flags_tpm_path = args.fequals_str(
"--tpm-path",
"-t",
"Specify a custom TPM device to use in /dev/tpmX format",
);
args.check_help();
if flags_tpm_path.is_empty() {
kv_set(libcros::keys::TPM_PATH, "/dev/tpm0");
} else {
kv_set(libcros::keys::TPM_PATH, &*flags_tpm_path);
}
Logger::init(verbose, true);
let tpm = kv_get(libcros::key_types::STRING, libcros::keys::TPM_PATH);
LOG!("resume on {:?}", tpm);
let rc = TlclResume();
#[cfg(feature = "tpm2_0")]
if rc == TPM_RC_INITIALIZE {
LOG!("resume skipped: already initialized (rc={:x})", rc);
return;
}
#[cfg(feature = "tpm1_2")]
if rc == TPM_E_INVALID_POSTINIT {
/* "(?)" here because i'm pretty sure this is just the same as TPM_RC_INITIALIZE on tpm2, but i'm not 100% sure */
LOG!("resume skipped: already initialized(?) (rc={:x})", rc);
return;
}
if rc != 0 {
LOG_FATAL!(rc.try_into().unwrap(); "TlclResume failed with error code: {:x}", rc);
}
}
----- END ./examples/tlcl_resume.rs -----
----- ./examples/tlcl_define_and_undefine.rs -----
#[cfg(feature = "tpm1_2")]
use libcros::tlcl::{TlclAssertPhysicalPresence, TlclPhysicalPresenceCMDEnable};
use libcros::{
LOG, Logger, kv_get, kv_set,
libargs::ArgCheck,
tlcl::{
TlclDefineSpace, TlclUndefineSpace,
permissions::{
NV_PERM_AUTHREAD, NV_PERM_AUTHWRITE, NV_PERM_OWNERREAD, NV_PERM_OWNERWRITE, NV_PERM_PPREAD,
NV_PERM_PPWRITE,
},
},
};
const NV_INDEX: u32 = 0x8000A;
const NV_SIZE: u32 = 0xA;
struct AuthType {
name: &'static str,
perm: u32,
}
const AUTH_TYPES: &[AuthType] = &[
AuthType {
name: "PPWRITE | PPREAD",
perm: NV_PERM_PPWRITE | NV_PERM_PPREAD,
},
AuthType {
name: "OWNERWRITE | OWNERREAD",
perm: NV_PERM_OWNERWRITE | NV_PERM_OWNERREAD,
},
AuthType {
name: "AUTHWRITE | AUTHREAD",
perm: NV_PERM_AUTHWRITE | NV_PERM_AUTHREAD,
},
AuthType {
name: "PPWRITE | AUTHREAD",
perm: NV_PERM_PPWRITE | NV_PERM_AUTHREAD,
},
AuthType {
name: "AUTHWRITE | PPREAD",
perm: NV_PERM_AUTHWRITE | NV_PERM_PPREAD,
},
];
fn main() {
let mut args: ArgCheck = ArgCheck::new();
let verbose: bool = args.fbool("--verbose", "", "Enable debug messages");
let flags_tpm_path = args.fequals_str(
"--tpm-path",
"-t",
"Specify a custom TPM device to use in /dev/tpmX format",
);
args.check_help();
if flags_tpm_path.is_empty() {
kv_set(libcros::keys::TPM_PATH, "/dev/tpm0");
} else {
kv_set(libcros::keys::TPM_PATH, &*flags_tpm_path);
}
Logger::init(verbose, true);
let tpm = kv_get(libcros::key_types::STRING, libcros::keys::TPM_PATH);
LOG!("using {:?}", tpm);
/* TPM 1.2 requires physical presence for DefineSpace with PPWRITE/PPREAD */
#[cfg(feature = "tpm1_2")]
{
let rc = TlclPhysicalPresenceCMDEnable();
LOG!("PhysicalPresenceCMDEnable rc: {:X}", rc);
let rc = TlclAssertPhysicalPresence();
LOG!("AssertPhysicalPresence rc: {:X}", rc);
}
for auth in AUTH_TYPES {
LOG!("--| {} (perm=0x{:X}) |--", auth.name, auth.perm);
let rc = TlclDefineSpace(NV_INDEX, auth.perm, NV_SIZE);
LOG!("DefineSpace rc: {:X}", rc);
if rc != 0 {
LOG!("skipping undefine");
continue;
}
let rc = TlclUndefineSpace(NV_INDEX);
LOG!("UndefineSpace rc: {:X}", rc);
}
LOG!("done");
}
----- END ./examples/tlcl_define_and_undefine.rs -----
----- ./examples/tlcl_write_with_offset.rs -----
use libcros::{
LOG, LOG_FATAL, Logger, kv_get, kv_set, libargs::ArgCheck, tlcl::TlclWriteWithOffset,
};
const NV_INDEX: u32 = 0x1008;
// kernver 0x00010003
const DATA: [u8; 12] = [
0x4C, 0x57, 0x52, 0x47, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xEC,
];
fn main() {
let mut args: ArgCheck = ArgCheck::new();
let verbose: bool = args.fbool("--verbose", "", "Enable debug messages");
let flags_tpm_path = args.fequals_str(
"--tpm-path",
"-t",
"Specify a custom TPM device to use in /dev/tpmX format",
);
args.check_help();
if flags_tpm_path.is_empty() {
kv_set(libcros::keys::TPM_PATH, "/dev/tpm0");
} else {
kv_set(libcros::keys::TPM_PATH, &*flags_tpm_path);
}
Logger::init(verbose, true);
let tpm = kv_get(libcros::key_types::STRING, libcros::keys::TPM_PATH);
LOG!(
"writing {} bytes to index {} on {:?}",
DATA.len(),
NV_INDEX,
tpm
);
let rc = TlclWriteWithOffset(
NV_INDEX,
DATA.as_ptr() as *const core::ffi::c_void,
DATA.len() as u32,
0x1,
);
if rc != 0 {
LOG_FATAL!(rc.try_into().unwrap(); "TlclWrite failed with error code: {:x}", rc);
}
LOG!("write succeeded");
}
----- END ./examples/tlcl_write_with_offset.rs -----
----- ./src/gsc/constants.rs -----
/* Copyright 2015 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#![allow(non_camel_case_types, non_upper_case_globals, dead_code)]
macro_rules! BIT {
($nr:expr) => {
1usize << $nr
};
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum vendor_cmd_cc {
/* Original extension commands */
EXTENSION_AES = 0,
EXTENSION_HASH = 1,
EXTENSION_RSA = 2,
EXTENSION_ECC = 3,
EXTENSION_FW_UPGRADE = 4,
EXTENSION_HKDF = 5,
EXTENSION_ECIES = 6,
EXTENSION_POST_RESET = 7,
LAST_EXTENSION_COMMAND = 15,
/* Our TPMv2 vendor-specific command codes. 16 bits available. */
VENDOR_CC_GET_LOCK = 16,
VENDOR_CC_SET_LOCK = 17,
VENDOR_CC_SYSINFO = 18,
/*
* VENDOR_CC_IMMEDIATE_RESET may have an argument, which is a (uint16_t)
* time delay (in milliseconds) in doing a reset. Max value is 1000.
* The command may also be called without an argument, which will be
* regarded as zero time delay.
*/
VENDOR_CC_IMMEDIATE_RESET = 19,
VENDOR_CC_INVALIDATE_INACTIVE_RW = 20,
VENDOR_CC_COMMIT_NVMEM = 21,
/* DEPRECATED(22): deep sleep control command. */
VENDOR_CC_REPORT_TPM_STATE = 23,
VENDOR_CC_TURN_UPDATE_ON = 24,
VENDOR_CC_GET_BOARD_ID = 25,
VENDOR_CC_SET_BOARD_ID = 26,
VENDOR_CC_U2F_APDU = 27,
VENDOR_CC_POP_LOG_ENTRY = 28,
VENDOR_CC_GET_REC_BTN = 29,
VENDOR_CC_RMA_CHALLENGE_RESPONSE = 30,
/* DEPRECATED(31): CCD password command (now part of VENDOR_CC_CCD) */
/*
* Disable factory mode. Reset all ccd capabilities to default and reset
* write protect to follow battery presence.
*/
VENDOR_CC_DISABLE_FACTORY = 32,
/* DEPRECATED(33): Manage CCD password phase */
VENDOR_CC_CCD = 34,
VENDOR_CC_GET_ALERTS_DATA = 35,
VENDOR_CC_SPI_HASH = 36,
VENDOR_CC_PINWEAVER = 37,
/*
* Check the factory reset settings. If they're all set correctly, do a
* factory reset to enable ccd factory mode. All capabilities will be
* set to Always and write protect will be permanently disabled. This
* mode can't be reset unless VENDOR_CC_DISABLE_FACTORY is called or
* the 'ccd reset' console command is run.
*/
VENDOR_CC_RESET_FACTORY = 38,
/*
* Get the write protect setting. This will return a single byte with
* bits communicating the write protect setting as described by the
* WPV subcommands.
*/
VENDOR_CC_WP = 39,
/*
* Either enable or disable TPM mode. This is allowed for one-time only
* until next TPM reset EVENT. In other words, once TPM mode is set,
* then it cannot be altered to the other mode value. The allowed input
* values are either TPM_MODE_ENABLED or TPM_MODE_DISABLED as defined
* in 'enum tpm_modes', tpm_registers.h.
* If the input size is zero, it won't change TPM_MODE.
* If either the input size is zero or the input value is valid,
* it will respond with the current tpm_mode value in uint8_t format.
*
* Return code:
* VENDOR_RC_SUCCESS: completed successfully.
* VENDOR_RC_INTERNAL_ERROR: failed for an internal reason.
* VENDOR_RC_NOT_ALLOWED: failed in changing TPM_MODE,
* since it is already set.
* VENDOR_RC_NO_SUCH_SUBCOMMAND: failed because the given input
* is undefined.
*/
VENDOR_CC_TPM_MODE = 40,
/*
* Initializes INFO1 SN data space, and sets SN hash. Takes three
* int32 as parameters, which are written as the SN hash.
*/
VENDOR_CC_SN_SET_HASH = 41,
/*
* Increments the RMA count in the INFO1 SN data space. The space must
* have been previously initialized with the _SET_HASH command above for
* this to succeed. Takes one byte as parameter, which indicates the
* number to increment the RMA count by; this is typically 1 or 0.
*
* Incrementing the RMA count by 0 will set the RMA indicator, but not
* incremement the count. This is useful to mark that a device has been
* RMA'd, but that we were not able to log the new serial number.
*
* Incrementing the count by the maximum RMA count (currently 7) will
* always set the RMA count to the maximum value, regardless of the
* previous value. This can be used with any device, regardless of
* current state, to mark it as RMA'd but with an unknown RMA count.
*/
VENDOR_CC_SN_INC_RMA = 42,
/*
* Gets the latched state of a power button press to indicate user
* recent user presence. The power button state is automatically cleared
* after PRESENCE_TIMEOUT.
*/
VENDOR_CC_GET_PWR_BTN = 43,
/*
* U2F commands.
*/
VENDOR_CC_U2F_GENERATE = 44,
VENDOR_CC_U2F_SIGN = 45,
VENDOR_CC_U2F_ATTEST = 46,
VENDOR_CC_FLOG_TIMESTAMP = 47,
VENDOR_CC_ENDORSEMENT_SEED = 48,
VENDOR_CC_U2F_MODE = 49,
/*
* HMAC-SHA256 DRBG invocation for ACVP tests
*/
VENDOR_CC_DRBG_TEST = 50,
VENDOR_CC_TRNG_TEST = 51,
/* EC EFS(Early Firmware Selection) commands */
VENDOR_CC_GET_BOOT_MODE = 52,
VENDOR_CC_RESET_EC = 53,
VENDOR_CC_SEED_AP_RO_CHECK = 54,
VENDOR_CC_FIPS_CMD = 55,
VENDOR_CC_GET_AP_RO_HASH = 56,
VENDOR_CC_GET_AP_RO_STATUS = 57,
VENDOR_CC_AP_RO_VALIDATE = 58,
/*
* Vendor command to disable deep sleep during the next TPM_RST_L
* assertion. Cr50 used to use 22 to do this. It can't reuse that
* because some old boards still send it, and deep sleep shouldn't
* be disabled on those boards.
*/
VENDOR_CC_DS_DIS_TEMP = 59,
VENDOR_CC_USER_PRES = 60,
/* POP_LOG_ENTRY with a 64 bit previous timestamp in ms */
VENDOR_CC_POP_LOG_ENTRY_MS = 61,
/*
* Get/set AP RO configuration settings
*
* The message sent and received to this vendor command,
* with the exception * of SET responses, uses the
* following form:
*
* ```c
* struct __attribute__((__packed__)) command_msg {
* // Current version of the API
* uint8_t version;
* // Determines payload type, see
* // `arv_config_setting_command_e`.
* uint8_t command;
* // Type here depends on command
* struct command_data data;
* };
* ```
*/
VENDOR_CC_GET_AP_RO_VERIFY_SETTING = 62,
VENDOR_CC_SET_AP_RO_VERIFY_SETTING = 63,
/* Ti50 only. */
VENDOR_CC_SET_CAPABILITY = 64,
VENDOR_CC_GET_TI50_STATS = 65,
VENDOR_CC_GET_CRASHLOG = 66,
VENDOR_CC_GET_CONSOLE_LOGS = 67,
VENDOR_CC_GET_FACTORY_CONFIG = 68,
VENDOR_CC_SET_FACTORY_CONFIG = 69,
VENDOR_CC_GET_TIME = 70,
VENDOR_CC_GET_BOOT_TRACE = 71,
VENDOR_CC_GET_CHASSIS_OPEN = 72,
/*
* 72 was also the old VENDOR_CC_GET_CR50_METRICS value. It was moved
* to avoid conflict with ti50.
*/
VENDOR_CC_GET_CR50_METRICS = 73,
/*
* Used for UMA collection for feature launch. After feature launch,
* this can be removed as long as the value is reserved.
* Cr50 doesn.t use this.
* VENDOR_CC_GET_AP_RO_RESET_COUNTS = 74,
*/
/* Returns info to identify the specific GSC chip type. */
VENDOR_CC_GET_CHIP_ID = 75,
/* Loads and stores superblock macs for the Trusty storage application.
*/
VENDOR_CC_TRUSTY_STORAGE_MAC = 77,
LAST_VENDOR_COMMAND = 65535,
}
pub const CONFIG_EXTENSION_COMMAND: u32 = 0xbaccd00a;
pub const TPM_CC_VENDOR_BIT_MASK: u32 = 0x20000000;
/// vendor_cmd_rc can't be an enum due to duplicate discriminants between
/// the EXTENSION_HASH error codes and the VENDOR_RC codes.
pub type vendor_cmd_rc = u32;
/* EXTENSION_HASH error codes */
/* Attempt to start a session on an active handle. */
pub const EXC_HASH_DUPLICATED_HANDLE: vendor_cmd_rc = 1;
pub const EXC_HASH_TOO_MANY_HANDLES: vendor_cmd_rc = 2; /* No room to allocate a new context. */
/* Continuation/finish on unknown context. */
pub const EXC_HASH_UNKNOWN_CONTEXT: vendor_cmd_rc = 3;
/* Our TPMv2 vendor-specific response codes. */
pub const VENDOR_RC_SUCCESS: vendor_cmd_rc = 0;
pub const VENDOR_RC_BOGUS_ARGS: vendor_cmd_rc = 1;
pub const VENDOR_RC_READ_FLASH_FAIL: vendor_cmd_rc = 2;
pub const VENDOR_RC_WRITE_FLASH_FAIL: vendor_cmd_rc = 3;
pub const VENDOR_RC_REQUEST_TOO_BIG: vendor_cmd_rc = 4;
pub const VENDOR_RC_RESPONSE_TOO_BIG: vendor_cmd_rc = 5;
pub const VENDOR_RC_INTERNAL_ERROR: vendor_cmd_rc = 6;
pub const VENDOR_RC_NOT_ALLOWED: vendor_cmd_rc = 7;
pub const VENDOR_RC_NO_SUCH_SUBCOMMAND: vendor_cmd_rc = 8;
pub const VENDOR_RC_IN_PROGRESS: vendor_cmd_rc = 9;
pub const VENDOR_RC_PASSWORD_REQUIRED: vendor_cmd_rc = 10;
pub const VENDOR_RC_NVMEM_LOCKED: vendor_cmd_rc = 11;
/* Maximum possible failure reason. */
pub const VENDOR_RC_NO_SUCH_COMMAND: vendor_cmd_rc = 127;
/*
* Bits 10 and 8 set, this is to be ORed with the rest of the error
* values to make the combined value compliant with the spec
* requirements.
*/
pub const VENDOR_RC_ERR: vendor_cmd_rc = 0x500;
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum tpm_modes {
TPM_MODE_ENABLED_TENTATIVE = 0,
TPM_MODE_ENABLED = 1,
TPM_MODE_DISABLED = 2,
TPM_MODE_MAX = 3,
}
/* Various upgrade command return values. */
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum return_value {
UPGRADE_SUCCESS = 0,
UPGRADE_BAD_ADDR = 1,
UPGRADE_ERASE_FAILURE = 2,
UPGRADE_DATA_ERROR = 3,
UPGRADE_WRITE_FAILURE = 4,
UPGRADE_VERIFY_ERROR = 5,
UPGRADE_GEN_ERROR = 6,
UPGRADE_MALLOC_ERROR = 7,
UPGRADE_ROLLBACK_ERROR = 8,
UPGRADE_RATE_LIMIT_ERROR = 9,
UPGRADE_UNALIGNED_BLOCK_ERROR = 10,
UPGRADE_TRUNCATED_HEADER_ERROR = 11,
UPGRADE_BOARD_ID_ERROR = 12,
UPGRADE_BOARD_FLAGS_ERROR = 13,
UPGRADE_DEV_ID_MISMATCH_ERROR = 14,
}
/* Returns info to identify the specific GSC chip type. */
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct get_chip_id_response {
pub tpm_did_vid: u32,
pub chip_id: u32,
}
/*
* Type of the GSC device. This is used to represent which type of GSC we are
* connected to and to tag an image file for compatibility.
* for downloading.
*/
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum gsc_device {
GSC_DEVICE_H1 = 0,
GSC_DEVICE_CT = 1,
GSC_DEVICE_DT = 2,
GSC_DEVICE_NT = 3,
}
/*****************************************************************************/
/* Ti50 Specific Structs */
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct ti50_stats_v0 {
/* filesystem initialization time in ms */
pub fs_init_time: u32,
/* filesustem usage in bytes */
pub fs_usage: u32,
/* AP RO verification time in ms */
pub aprov_time: u32,
/* combination of AP RO verification result and failure reason, used by
* UMA
*/
pub expanded_aprov_status: u32,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct ti50_stats_v1 {
pub stats: ti50_stats_v0,
/* [31:27] - bits used
* [27: 4] - unused
* [ 3: 3] - CCD_MODE
* [ 2: 2] - rdd keep alive at boot
* [ 1: 0] - rdd keep alive state
*/
pub misc_status: u32,
}
/*
* Keep in sync with
* ti50/common/applications/sys_mgr/src/tpm_vendor/metrics.rs
* The latest time new fields were added as version 2.
*/
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct ti50_stats {
pub v1: ti50_stats_v1,
pub version: u32,
pub filesystem_busy_count: u32,
pub crypto_busy_count: u32,
pub dispatcher_busy_count: u32,
pub timeslices_expired: u32,
pub crypto_init_time: u32,
}
pub const METRICSV_BITS_USED_SHIFT: u32 = 27;
pub const METRICSV_RDD_KEEP_ALIVE_MASK: u32 = 3;
pub const METRICSV_RDD_KEEP_ALIVE_AT_BOOT_SHIFT: u32 = 2;
pub const METRICSV_RDD_KEEP_ALIVE_AT_BOOT_MASK: u32 = 1 << METRICSV_RDD_KEEP_ALIVE_AT_BOOT_SHIFT;
pub const METRICSV_CCD_MODE_SHIFT: u32 = 3;
pub const METRICSV_CCD_MODE_MASK: u32 = 1 << METRICSV_CCD_MODE_SHIFT;
pub const METRICSV_WP_ASSERTED_SHIFT: u32 = 4;
pub const METRICSV_WP_ASSERTED_MASK: u32 = 1 << METRICSV_WP_ASSERTED_SHIFT;
pub const METRICSV_ALLOW_UNVERIFIED_RO_SHIFT: u32 = 5;
pub const METRICSV_ALLOW_UNVERIFIED_RO_MASK: u32 = 1 << METRICSV_ALLOW_UNVERIFIED_RO_SHIFT;
pub const METRICSV_IS_PROD_SHIFT: u32 = 6;
pub const METRICSV_IS_PROD_MASK: u32 = 1 << METRICSV_IS_PROD_SHIFT;
pub const METRICSV_RDD_IS_DETECTED_SHIFT: u32 = 7;
pub const METRICSV_RDD_IS_DETECTED_MASK: u32 = 1 << METRICSV_RDD_IS_DETECTED_SHIFT;
/* End Ti50 Specific Structs */
/*****************************************************************************/
/*
* VENDOR_CC_WP options, only WP_ENABLE is accepted for cr50. For ti50,
* enable, disable, and follow are all supported.
*/
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum wp_options {
WP_NONE = 0,
WP_CHECK = 1,
WP_ENABLE = 2,
WP_DISABLE = 3,
WP_FOLLOW = 4,
}
/*
* Subcommand code, used to set write protect.
*/
pub const WPV_UPDATE: usize = BIT!(0);
pub const WPV_ENABLE: usize = BIT!(1);
pub const WPV_FORCE: usize = BIT!(2);
pub const WPV_ATBOOT_SET: usize = BIT!(3);
pub const WPV_ATBOOT_ENABLE: usize = BIT!(4);
pub const WPV_FWMP_FORCE_WP_EN: usize = BIT!(5);
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct signed_header_version {
pub minor: u32,
pub major: u32,
pub epoch: u32,
}
/// Packet to be recieved during connection establishment.
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct first_response_pdu {
pub return_value: u32,
/* The below fields are present in versions 2 and up. */
pub protocol_version: u32,
/* The below fields are present in versions 3 and up. */
pub backup_ro_offset: u32,
pub backup_rw_offset: u32,
/* The below fields are present in versions 4 and up. */
/* Versions of the currently active RO and RW sections. */
pub shv: [signed_header_version; 2],
/* The below fields are present in versions 5 and up */
/* keyids of the currently active RO and RW sections. */
pub keyid: [u32; 2],
}
/// Response from VENDOR_CC_SYSINFO
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct sys_info_repsonse {
pub ro_keyid: u32,
pub rw_keyid: u32,
pub dev_id0: u32,
pub dev_id1: u32,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct cr50_stats_response {
/* struct version number */
pub version: u32,
/* Source of last reset. */
pub reset_src: u32,
/* Board properties for current boot. */
pub brdprop: u32,
/* Misc status.
* [31: 5] - unused
* [ 4] - ambiguous brdprop
* [ 3] - rddkeepalive atboot state
* [ 2] - CCD_MODE enabled
* [ 1] - rdd keep alive state
* [ 0] - rdd detected
*/
pub misc_status: u32,
/* Time since last cr50 reset */
pub reset_time_s: u32,
/* Time since last cold reset */
pub cold_reset_time_s: u32,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct board_id {
pub board_type: u32,
pub type_inv: u32,
pub flags: u32,
}
const F_TAG: usize = size_of::<u16>(); // 2
const F_SUBCMD: usize = size_of::<u16>(); // 2
const F_LEN: usize = size_of::<u32>(); // 4
const F_CODE: usize = size_of::<u32>(); // 4
const F_DIGEST: usize = size_of::<u32>(); // 4
const F_ADDR: usize = size_of::<u32>(); // 4
pub const TPM_ORDINAL_OFFSET: usize = F_TAG + F_LEN;
pub const TPM_SUBCMD_OFFSET: usize = TPM_ORDINAL_OFFSET + F_CODE;
pub const TPM_PKT_CMD_HEADER_SIZE: usize = TPM_SUBCMD_OFFSET + F_SUBCMD;
pub const TPM_PKT_UPG_HEADER_SIZE: usize = TPM_PKT_CMD_HEADER_SIZE + F_DIGEST + F_ADDR;
pub const MAX_RX_BUF_SIZE: usize = 2048;
pub const SIGNED_TRANSFER_SIZE: usize = 1024;
pub const MAX_TX_BUF_SIZE: usize = SIGNED_TRANSFER_SIZE + TPM_PKT_UPG_HEADER_SIZE;
----- END ./src/gsc/constants.rs -----
----- ./src/gsc/mod.rs -----
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
}
----- END ./src/gsc/mod.rs -----
----- ./src/keyval/get.rs -----
use crate::keyval::{KvValue, kv};
pub fn kv_get(ty: &str, key: &'static str) -> Option<KvValue> {
let map = kv().lock().unwrap();
let val = map.get(key)?;
match (ty, val) {
("string", KvValue::String(_)) => Some(clone_string(val)),
("int", KvValue::Int(_)) => Some(clone_int(val)),
("bool", KvValue::Bool(_)) => Some(clone_bool(val)),
("socket", KvValue::Socket(_)) => None,
("file", KvValue::File(f)) => f.try_clone().ok().map(KvValue::File),
_ => None,
}
}
fn clone_string(v: &KvValue) -> KvValue {
if let KvValue::String(s) = v {
KvValue::String(s.clone())
} else {
unreachable!()
}
}
fn clone_int(v: &KvValue) -> KvValue {
if let KvValue::Int(i) = v {
KvValue::Int(*i)
} else {
unreachable!()
}
}
fn clone_bool(v: &KvValue) -> KvValue {
if let KvValue::Bool(b) = v {
KvValue::Bool(*b)
} else {
unreachable!()
}
}
----- END ./src/keyval/get.rs -----
----- ./src/keyval/mod.rs -----
/// Common keys for the global key/value store.
pub mod keys {
/// Internal disk path.
pub const INTERNAL_DISK: &str = "internal_disk";
/// Global file handle for the GSC device.
pub const GSC_SOCKET: &str = "gsc_socket";
#[cfg(feature = "tlcl")]
/// TPM device path.
pub const TPM_PATH: &str = "tpm_path";
#[cfg(feature = "tlcl")]
#[cfg(feature = "tpm2_0")]
pub const TPM_TAG: &str = "tpm_tag";
#[cfg(feature = "tlcl")]
#[cfg(feature = "tpm2_0")]
/// Cached physical hierarchy state.
pub const PH_DISABLED: &str = "ph_disabled";
/// Used for example-specific features.
#[cfg(feature = "example")]
pub const EXAMPLE: &str = "example";
}
use std::{
collections::HashMap,
fs::File,
os::unix::net::UnixStream,
sync::{Mutex, OnceLock},
};
pub mod erase;
pub mod get;
pub mod set;
pub use erase::kv_erase;
pub use get::kv_get;
pub use set::kv_set;
#[derive(Debug)]
pub enum KvValue {
String(String),
Int(i64),
Bool(bool),
Socket(UnixStream),
File(File),
}
pub mod key_types {
pub const STRING: &str = "string";
pub const INT: &str = "int";
pub const BOOL: &str = "bool";
pub const SOCKET: &str = "socket";
pub const FILE: &str = "file";
}
static KV: OnceLock<Mutex<HashMap<&'static str, KvValue>>> = OnceLock::new();
pub fn kv() -> &'static Mutex<HashMap<&'static str, KvValue>> {
KV.get_or_init(|| Mutex::new(HashMap::new()))
}
----- END ./src/keyval/mod.rs -----
----- ./src/keyval/set.rs -----
use std::{fs::File, os::unix::net::UnixStream};
use crate::keyval::{KvValue, kv};
pub fn kv_set(key: &'static str, val: impl Into<KvValue>) {
kv().lock().unwrap().insert(key, val.into());
}
impl From<String> for KvValue {
fn from(v: String) -> Self {
KvValue::String(v)
}
}
impl From<&str> for KvValue {
fn from(v: &str) -> Self {
KvValue::String(v.to_string())
}
}
impl From<i64> for KvValue {
fn from(v: i64) -> Self {
KvValue::Int(v)
}
}
impl From<bool> for KvValue {
fn from(v: bool) -> Self {
KvValue::Bool(v)
}
}
impl From<UnixStream> for KvValue {
fn from(v: UnixStream) -> Self {
KvValue::Socket(v)
}
}
impl From<File> for KvValue {
fn from(v: File) -> Self {
KvValue::File(v)
}
}
impl From<u8> for KvValue {
fn from(v: u8) -> Self {
KvValue::Int(v as i64)
}
}
impl From<u16> for KvValue {
fn from(v: u16) -> Self {
KvValue::Int(v as i64)
}
}
impl From<u32> for KvValue {
fn from(v: u32) -> Self {
KvValue::Int(v as i64)
}
}
----- END ./src/keyval/set.rs -----
----- ./src/keyval/erase.rs -----
use crate::keyval::kv;
pub fn kv_erase(key: &'static str) {
kv().lock().unwrap().remove(key);
}
----- END ./src/keyval/erase.rs -----
----- ./src/diskutils/mod.rs -----
pub mod gpt;
----- END ./src/diskutils/mod.rs -----
----- ./src/diskutils/gpt.rs -----
use std::io::{self, Read, Seek, SeekFrom};
use crate::structs::{read_struct, write_struct, GPTHeader, GPTPartitionEntry};
const EMPTY_GUID: [u8; 16] = [0u8; 16];
const GPT_HEADER_LBA: u64 = 1; /* https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_table_header_(LBA_1) */
const SECTOR_SIZE: u64 = 512; /* This is the default sector size. Maybe we shouldn't be hardcoding it? */
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Uuid([u8; 16]);
impl Uuid {
pub fn from_bytes(bytes: [u8; 16]) -> Self {
Self(bytes)
}
pub fn as_bytes(&self) -> &[u8; 16] {
&self.0
}
}
pub fn gpt_guid_to_uuid(raw: &[u8; 16]) -> Uuid {
let mut bytes = *raw;
bytes[0..4].reverse();
bytes[4..6].reverse();
bytes[6..8].reverse();
Uuid::from_bytes(bytes)
}
pub fn uuid_to_gpt_guid(uuid: &Uuid) -> [u8; 16] {
let mut bytes = *uuid.as_bytes();
bytes[0..4].reverse();
bytes[4..6].reverse();
bytes[6..8].reverse();
bytes
}
pub struct GptDisk {
pub header: GPTHeader,
pub entries: Vec<GPTPartitionEntry>,
}
impl GptDisk {
fn partition_index(id: u32) -> Option<usize> {
if id == 0 {
return None;
}
Some(id as usize - 1)
}
fn entry_type_guid(entry: &GPTPartitionEntry) -> [u8; 16] {
let field_ptr = core::ptr::addr_of!(entry.partition_type_guid) as *const u8;
let field_bytes =
unsafe { core::slice::from_raw_parts(field_ptr, core::mem::size_of::<[u8; 16]>()) };
read_struct(field_bytes)
}
fn is_used_entry(entry: &GPTPartitionEntry) -> bool {
Self::entry_type_guid(entry) != EMPTY_GUID
}
pub fn partition(&self, id: u32) -> Option<&GPTPartitionEntry> {
let index = Self::partition_index(id)?;
let entry = self.entries.get(index)?;
if !Self::is_used_entry(entry) {
return None;
}
Some(entry)
}
pub fn partition_mut(&mut self, id: u32) -> Option<&mut GPTPartitionEntry> {
let index = Self::partition_index(id)?;
self.entries.get_mut(index)
}
pub fn partitions_by_type(&self, type_uuid: &Uuid) -> Vec<(u32, &GPTPartitionEntry)> {
let target = uuid_to_gpt_guid(type_uuid);
self
.entries
.iter()
.enumerate()
.filter_map(|(i, entry)| {
if Self::entry_type_guid(entry) != target {
return None;
}
Some((i as u32 + 1, entry))
})
.collect()
}
pub fn has_partition_type(&self, type_uuid: &Uuid) -> bool {
let target = uuid_to_gpt_guid(type_uuid);
self
.entries
.iter()
.any(|entry| Self::entry_type_guid(entry) == target)
}
pub fn partition_with_type(&self, id: u32, type_uuid: &Uuid) -> Option<&GPTPartitionEntry> {
let target = uuid_to_gpt_guid(type_uuid);
let entry = self.partition(id)?;
if Self::entry_type_guid(entry) != target {
return None;
}
Some(entry)
}
}
pub fn serialize_entries(disk: &GptDisk) -> Vec<u8> {
let entry_size = disk.header.size_partition_entry as usize;
if entry_size == 0 {
return Vec::new();
}
let mut raw = vec![0u8; disk.entries.len() * entry_size];
for (i, entry) in disk.entries.iter().enumerate() {
write_struct(entry, &mut raw[i * entry_size..]);
}
raw
}
pub fn read_header<R: Read + Seek>(disk: &mut R) -> io::Result<GPTHeader> {
disk.seek(SeekFrom::Start(GPT_HEADER_LBA * SECTOR_SIZE))?;
let header_size = core::mem::size_of::<GPTHeader>();
let mut header_bytes = vec![0u8; header_size];
disk.read_exact(&mut header_bytes)?;
Ok(read_struct(&header_bytes))
}
----- END ./src/diskutils/gpt.rs -----
----- ./src/vpd/mod.rs -----
/*
pub fn set_vpd_value(partition, key, value) -> ();
pub fn get_vpd_value(partition, key) -> String;
pub fn del_vpd_key(partition, key) -> ();
pub fn erase_vpd_partition(partition) -> ();
pub fn get_vpd_keys(partition) -> Vec<String>;
partition will only ever be "RO_VPD" or "RW_VPD"
we need to use libflashrom.
*/
pub mod flashrom;----- END ./src/vpd/mod.rs -----
----- ./src/vpd/flashrom.rs -----
#![allow(non_snake_case)]
pub fn FlashromRead() {}
----- END ./src/vpd/flashrom.rs -----
----- ./src/execute.rs -----
use std::process::{Command, ExitStatus, Stdio};
fn bash_command(command: &str, live_output: bool) -> Command {
let mut cmd = Command::new("bash");
cmd.arg("-c").arg(command);
if live_output {
cmd
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit());
}
cmd
}
/// Run a shell command.
/// Returns output text or error text.
pub fn execute_cmd_stdio(command: &str, live_output: bool) -> String {
if live_output {
let status = bash_command(command, true).status();
match status {
Ok(s) if s.success() => "".to_string(),
Ok(s) => format!("process exited with status: {}", s),
Err(e) => format!("error executing command: {}", e),
}
} else {
match bash_command(command, false).output() {
Ok(output) => {
if output.status.success() {
String::from_utf8_lossy(&output.stdout).to_string()
} else {
String::from_utf8_lossy(&output.stderr).to_string()
}
}
Err(e) => format!("error executing command: {}", e),
}
}
}
/// Run a shell command and return exit code.
/// Returns -1 on failure.
pub fn execute_cmd_rc(command: &str, live_output: bool) -> i32 {
if live_output {
match bash_command(command, true).status() {
Ok(s) => s.code().unwrap_or(-1),
Err(_) => -1,
}
} else {
match bash_command(command, false).output() {
Ok(output) => output.status.code().unwrap_or(-1),
Err(_) => -1,
}
}
}
/// Spawn /bin/bash and wait for exit.
pub fn spawn_bash_shell() -> ExitStatus {
let err = Command::new("/bin/bash")
.spawn()
.expect("failed to spawn bash")
.wait()
.expect("failed to wait on bash");
return err;
}
----- END ./src/execute.rs -----
----- ./src/lib.rs -----
/// Easy-to-use key=value system with types.
pub mod keyval;
pub use keyval::*;
/// Easy-to-use logging API
pub mod logging;
pub use logging::Logger;
/// Basic cryptography functions
pub mod crypto;
/// Easy-to-use functions to execute commands
pub mod execute;
/// Lightweight argument parser
pub mod libargs;
/// Commonly-used structs
pub mod structs;
/// High-level functions to get information about a Chrome device
pub mod sysinfo;
/// Send/receive vendor commands to/from the GSC (Google Security Chip) / TPM.
pub mod gsc;
/// High-level functions to interact with a GPT-formatted disk. (Including ChromeOS disks)
pub mod diskutils;
/*
Anything that requires a dependency should be locked behind a feature flag.
*/
/// Basic drawing functions for TUIs
#[cfg(feature = "ui")]
pub mod ui;
/// A lightweight TPM2 library. Based on vboot's Tlcl library.
#[cfg(feature = "tlcl")]
pub mod tlcl;
/*
pub mod vpd;
*/
----- END ./src/lib.rs -----
----- ./src/logging.rs -----
static mut VERBOSE: bool = false;
/// Logger state for LOG macros.
pub struct Logger;
// use_colors isn't implemented at the moment, ignore it.
impl Logger {
/// Set logger state.
/// verbose enables LOG_DBG.
pub fn init(verbose: bool, _use_colors: bool) {
unsafe {
VERBOSE = verbose;
}
}
/// True when verbose logging is enabled.
pub fn verbose_enabled() -> bool {
unsafe { VERBOSE }
}
}
/// Info log.
#[macro_export]
macro_rules! LOG {
($($arg:tt)*) => {
println!(
"INFO [{}:{}]: {}",
file!(),
line!(),
format_args!($($arg)*)
);
};
}
/// Debug log when verbose is enabled.
#[macro_export]
macro_rules! LOG_DBG {
($($arg:tt)*) => {
if $crate::Logger::verbose_enabled() {
println!(
"DEBUG [{}:{}]: {}",
file!(),
line!(),
format_args!($($arg)*)
);
}
};
}
/// Fatal log and exit.
#[macro_export]
macro_rules! LOG_FATAL {
// LOG_FATAL!(67; "error"), would exit with rc 67
($rc:expr; $($arg:tt)*) => {{
println!(
"FATAL [{}:{}]: {}",
file!(),
line!(),
format_args!($($arg)*)
);
std::process::exit($rc);
}};
// LOG_FATAL!("error"), would exit with rc -1
($($arg:tt)*) => {{
println!(
"FATAL [{}:{}]: {}",
file!(),
line!(),
format_args!($($arg)*)
);
std::process::exit(-1);
}};
}
/// Fatal log without exit.
#[macro_export]
macro_rules! LOG_FATAL_NOEXIT {
// LOG_FATAL_NOEXIT!(42; "error"), sicne we dont exit, the rc is ignored.
($_rc:expr; $($arg:tt)*) => {
println!(
"FATAL [{}:{}]: {}",
file!(),
line!(),
format_args!($($arg)*)
);
};
// LOG_FATAL_NOEXIT!("error"), same thign, rc is ignored.
($($arg:tt)*) => {
println!(
"FATAL [{}:{}]: {}",
file!(),
line!(),
format_args!($($arg)*)
);
};
}
----- END ./src/logging.rs -----
----- ./src/structs.rs -----
#[cfg(feature = "tlcl")]
#[derive(Debug)]
pub struct TPM2B {
pub size: u16,
pub buffer: *const u8,
}
#[cfg(feature = "tlcl")]
#[derive(Debug)]
pub struct Tpm2SessionHeader {
pub session_handle: u32,
pub nonce_size: u16,
pub nonce: u8,
pub session_attrs: u8,
pub auth_size: u16,
pub auth: u8,
}
#[cfg(feature = "tlcl")]
#[derive(Debug)]
pub struct Tpm2TpmHeader {
pub tpm_tag: u16,
pub tpm_size: u32,
pub tpm_code: u32,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct GPTPartitionEntry {
pub partition_type_guid: [u8; 16],
pub unique_partition_guid: [u8; 16],
pub starting_lba: u64,
pub ending_lba: u64,
pub attribute_bits: u64,
pub partition_name: [u16; 36],
}
// lol thanks wikipedia
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct GPTHeader {
pub magic: [u8; 8],
pub revision: u32,
pub header_size: u32,
pub header_crc32: u32,
pub reserved0: u32,
pub current_lba: u64,
pub backup_lba: u64,
pub first_usable_lba: u64,
pub last_usable_lba: u64,
pub disk_guid: [u8; 16],
pub partition_entry_lba: u64,
pub num_partition_entries: u32,
pub size_partition_entry: u32,
pub partition_entry_array_crc32: u32,
}
#[derive(Debug)]
pub struct SysinfoRollbackVersionResponse {
pub rc: u32,
pub rollback_version: u32,
}
pub fn write_struct<T>(value: &T, out: &mut [u8]) {
let size = core::mem::size_of::<T>();
let bytes = size.min(out.len());
unsafe {
core::ptr::copy_nonoverlapping(value as *const T as *const u8, out.as_mut_ptr(), bytes);
}
}
pub fn read_struct<T: Copy>(input: &[u8]) -> T {
assert!(input.len() >= core::mem::size_of::<T>());
unsafe { core::ptr::read_unaligned(input.as_ptr() as *const T) }
}
----- END ./src/structs.rs -----
----- ./src/ui/constants.rs -----
pub const COLOR_RESET: &str = "\x1b[0m";
pub const COLOR_BLACK_B: &str = "\x1b[1;30m";
pub const COLOR_RED: &str = "\x1b[0;31m";
pub const COLOR_RED_B: &str = "\x1b[1;31m";
pub const COLOR_GREEN: &str = "\x1b[0;32m";
pub const COLOR_GREEN_B: &str = "\x1b[1;32m";
pub const COLOR_YELLOW: &str = "\x1b[0;33m";
pub const COLOR_YELLOW_B: &str = "\x1b[1;33m";
pub const COLOR_BLUE: &str = "\x1b[0;34m";
pub const COLOR_BLUE_B: &str = "\x1b[1;34m";
pub const COLOR_MAGENTA: &str = "\x1b[0;35m";
pub const COLOR_MAGENTA_B: &str = "\x1b[1;35m";
pub const COLOR_CYAN: &str = "\x1b[0;36m";
pub const COLOR_CYAN_B: &str = "\x1b[1;36m";
pub const COLOR_GREY: &str = "\x1b[0;90m";
pub const COLOR_GREY_B: &str = "\x1b[1;90m";
pub const COLOR_GRAY: &str = COLOR_GREY;
pub const COLOR_GRAY_B: &str = COLOR_GREY_B;
pub const COLOR_UNDERLINE: &str = "\x1b[4m";
pub const COLOR_ITALIC: &str = "\x1b[3m";
----- END ./src/ui/constants.rs -----
----- ./src/ui/mod.rs -----
/// Menu entry.
#[derive(Clone)]
pub struct MenuOption {
/// ANSI color code.
pub color_code: &'static str,
/// True if selectable.
pub enabled: bool,
/// Display text.
pub text: String,
}
impl MenuOption {
/// Create a menu option.
pub fn new(text: &str, enabled: bool, color_code: &'static str) -> Self {
Self {
color_code,
enabled,
text: text.to_string(),
}
}
}
pub mod constants;
pub mod header;
pub mod menu;
pub mod utils;
----- END ./src/ui/mod.rs -----
----- ./src/ui/utils.rs -----
#![allow(dead_code)]
use std::io::{self, Write};
use libc::{ECHO, ICANON, STDIN_FILENO, TCSANOW, tcgetattr, tcsetattr, termios};
pub fn strip_ansi(s: &str) -> std::borrow::Cow<'_, str> {
if !s.contains('\x1b') {
return std::borrow::Cow::Borrowed(s);
}
let mut out = String::with_capacity(s.len());
let mut chars = s.chars();
while let Some(c) = chars.next() {
if c == '\x1b' {
if chars.next() == Some('[') {
for ch in chars.by_ref() {
if ch.is_ascii_alphabetic() {
break;
}
}
}
} else {
out.push(c);
}
}
std::borrow::Cow::Owned(out)
}
pub fn char_display_width(c: char) -> usize {
match c as u32 {
/* control characters are zero-width */
0x0000..=0x001F | 0x007F => 0,
/* wide (East Asian) ranges */
0x1100..=0x115F
| 0x2329..=0x232A
| 0x2E80..=0x303E
| 0x3040..=0xA4CF
| 0xA960..=0xA97F
| 0xAC00..=0xD7FF
| 0xF900..=0xFAFF
| 0xFE10..=0xFE19
| 0xFE30..=0xFE6F
| 0xFF01..=0xFF60
| 0xFFE0..=0xFFE6
| 0x1B000..=0x1B0FF
| 0x1F300..=0x1F64F
| 0x1F900..=0x1FAFF
| 0x20000..=0x2FFFF
| 0x30000..=0x3FFFF => 2,
_ => 1,
}
}
pub fn str_display_width(s: &str) -> usize {
s.chars().map(char_display_width).sum()
}
/// Draw centered text in a box.
pub fn box_draw(text: &str) {
let margin = 5;
let lines: Vec<&str> = text.split('\n').collect();
let mut max_len = 0;
for line in &lines {
let stripped = strip_ansi(line);
let visual_width = str_display_width(stripped.as_ref());
if visual_width > max_len {
max_len = visual_width;
}
}
max_len += margin * 2;
println!("┌{}┐", "─".repeat(max_len));
for line in &lines {
let stripped = strip_ansi(line);
let visual_width = str_display_width(stripped.as_ref());
let pad_left = (max_len - visual_width) / 2;
let pad_right = max_len - visual_width - pad_left;
println!(
"│{}{}{}│",
" ".repeat(pad_left),
line,
" ".repeat(pad_right)
);
}
println!("└{}┘", "─".repeat(max_len));
}
/// Read one line from stdin.
pub fn input(prompt: &str) -> String {
print!("{}", prompt);
io::stdout().flush().unwrap();
let mut buffer = String::new();
io::stdin()
.read_line(&mut buffer)
.expect("Failed to read input");
buffer.trim().to_string()
}
/// Print continue prompt and wait for Enter.
pub fn enter_to_continue() {
println!("Press ENTER to continue!");
input("");
}
/// Enable terminal raw mode.
pub fn enable_raw_mode() -> libc::termios {
unsafe {
let mut termios: termios = std::mem::zeroed();
tcgetattr(STDIN_FILENO, &mut termios);
let original = termios;
termios.c_lflag &= !(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &termios);
original
}
}
/// Restore terminal settings.
pub fn disable_raw_mode(original: libc::termios) {
unsafe {
tcsetattr(STDIN_FILENO, TCSANOW, &original);
}
}
----- END ./src/ui/utils.rs -----
----- ./src/ui/header.rs -----
use crate::ui::utils::{box_draw, str_display_width, strip_ansi};
/// Build header text.
pub fn format_header(splash: &str, additional_text: &str) -> String {
let max_width = splash
.split('\n')
.map(|line| str_display_width(strip_ansi(line).as_ref()))
.max()
.unwrap_or(0);
let mut text = String::new();
text.push_str(splash.trim_end_matches('\n'));
text.push('\n');
text.push_str(&"─".repeat(max_width));
if !additional_text.trim().is_empty() {
text.push('\n');
text.push_str(additional_text);
if !additional_text.ends_with('\n') {
text.push('\n');
}
}
text
}
/// Draw boxed header.
pub fn ui_header(splash: &str, additional_text: &str) {
box_draw(format_header(splash, additional_text).trim_end());
}
----- END ./src/ui/header.rs -----
----- ./src/ui/menu.rs -----
use std::io::{self, Read, Write};
use crate::ui::{
MenuOption,
constants::COLOR_RESET,
utils::{disable_raw_mode, enable_raw_mode},
};
/// Arrow-key menu.
/// Returns None on Ctrl-C or input failure.
pub fn selection_menu(options: &[MenuOption]) -> Option<usize> {
if options.is_empty() {
return None;
}
let mut selected = 0;
let stdin = io::stdin();
let mut stdout = io::stdout();
let original_termios = enable_raw_mode();
loop {
print!("\r");
for (i, option) in options.iter().enumerate() {
if i == selected {
print!("{}> {} <{}", option.color_code, option.text, COLOR_RESET);
} else {
print!("{} {} {}", option.color_code, option.text, COLOR_RESET);
}
print!("\n");
}
print!("\x1b[J");
stdout.flush().unwrap();
let mut buf = [0u8; 1];
if stdin.lock().read_exact(&mut buf).is_err() {
break;
}
match buf[0] {
b'\n' | b'\r' => {
if options[selected].enabled {
stdout.flush().unwrap();
disable_raw_mode(original_termios);
print!("\n");
return Some(selected);
}
}
0x1b => {
let mut seq = [0u8; 2];
if stdin.lock().read_exact(&mut seq).is_ok() && seq[0] == b'[' {
match seq[1] {
b'A' => {
if selected > 0 {
selected -= 1;
}
}
b'B' => {
if selected < options.len() - 1 {
selected += 1;
}
}
_ => {}
}
}
}
0x03 => {
disable_raw_mode(original_termios);
return None;
}
_ => {}
}
for _ in 0..options.len() {
print!("\x1b[1A");
}
print!("\r");
stdout.flush().unwrap();
}
disable_raw_mode(original_termios);
None
}
----- END ./src/ui/menu.rs -----
----- ./src/sysinfo/gsc.rs -----
use crate::{
gsc::{
constants::{
board_id, first_response_pdu, signed_header_version,
vendor_cmd_cc::{EXTENSION_FW_UPGRADE, VENDOR_CC_GET_BOARD_ID},
},
read_response, send_command,
},
keyval::{KvValue, key_types, keys, kv_get},
};
const PDU_MAX: first_response_pdu = first_response_pdu {
return_value: u32::MAX,
protocol_version: u32::MAX,
backup_ro_offset: u32::MAX,
backup_rw_offset: u32::MAX,
shv: [
signed_header_version {
minor: u32::MAX,
major: u32::MAX,
epoch: u32::MAX,
},
signed_header_version {
minor: u32::MAX,
major: u32::MAX,
epoch: u32::MAX,
},
],
keyid: [u32::MAX, u32::MAX],
};
const BID_MAX: board_id = board_id {
board_type: u32::MAX,
type_inv: u32::MAX,
flags: u32::MAX,
};
pub fn get_gsc_version() -> first_response_pdu {
let mut f = match kv_get(key_types::FILE, keys::GSC_SOCKET) {
Some(KvValue::File(f)) => f,
_ => return PDU_MAX,
};
if !send_command(&mut f, 0, 0, &[], EXTENSION_FW_UPGRADE) {
return PDU_MAX;
}
let mut buf = [0u8; core::mem::size_of::<first_response_pdu>()];
if read_response(&mut f, &mut buf) != 0 {
return PDU_MAX;
}
unsafe { core::ptr::read_unaligned(buf.as_ptr() as *const first_response_pdu) }
}
pub fn get_gsc_board_id() -> board_id {
let mut f = match kv_get(key_types::FILE, keys::GSC_SOCKET) {
Some(KvValue::File(f)) => f,
_ => return BID_MAX,
};
if !send_command(&mut f, 0, 0, &[], VENDOR_CC_GET_BOARD_ID) {
return BID_MAX;
}
let mut buf = [0u8; core::mem::size_of::<board_id>()];
if read_response(&mut f, &mut buf) != 0 {
return BID_MAX;
}
unsafe { core::ptr::read_unaligned(buf.as_ptr() as *const board_id) }
}
----- END ./src/sysinfo/gsc.rs -----
----- ./src/sysinfo/mod.rs -----
#![allow(unused_imports)]
use crate::LOG_DBG;
pub mod backend;
pub mod gsc;
pub mod rollback;
pub mod stubs;
pub use gsc::*;
pub use rollback::*;
pub use stubs::*;
/* For functions that can't be categorized */
/// Returns the version of TPM that Tlcl was compiled with, e.g:
/// if feature `tpm 2_0` is enabled, then this will return "TPM 2.0".
#[cfg(feature = "tlcl")]
pub fn get_tpm_version() -> String {
crate::tlcl::TlclGetTPMVersion()
}
/// This is a stub for whenever Tlcl isn't enabled. Due to the fact that we
/// can't fetch the TPM version without the Tlcl library, we just stub it here.
#[cfg(not(feature = "tlcl"))]
pub fn get_tpm_version() -> String {
LOG_DBG!("tlcl feature not enabled");
"Tlcl was not enabled when compiling libcros.".to_string()
}
----- END ./src/sysinfo/mod.rs -----
----- ./src/sysinfo/rollback.rs -----
#![cfg_attr(not(feature = "tlcl"), allow(dead_code))] /* For NV index consts */
use crate::LOG_DBG;
#[cfg(feature = "tlcl")]
use crate::{structs::SysinfoRollbackVersionResponse, tlcl::TlclRead, tpm_nv_read};
const FIRMWARE_ROLLBACK_NV_INDEX: u32 = 0x1007;
const KERNEL_ROLLBACK_NV_INDEX: u32 = 0x1008;
const FIRMWARE_MANAGEMENT_PARAMETERS_NV_INDEX: u32 = 0x100A;
/// Fetch the active kernel rollback version from the TPM.
/// Returns u32::MAX (0xFFFFFFFF) on error.
#[cfg(feature = "tlcl")]
pub fn get_kernel_rollback_version() -> SysinfoRollbackVersionResponse {
tpm_nv_read!(KERNEL_ROLLBACK_NV_INDEX, 0x9, |buf: &[u8]| {
/*
OldUI and NewUI have different kernel version formats.
The first byte determines which one to use.
*/
match buf[0] {
0x02 => u32::from_le_bytes(buf[0x5..0x9].try_into().unwrap()),
0x10 => u32::from_le_bytes(buf[0x4..0x8].try_into().unwrap()),
_ => u32::MAX,
}
})
}
/// Fetch the active firmware rollback version from the TPM.
/// Returns u32::MAX (0xFFFFFFFF) on error.
#[cfg(feature = "tlcl")]
pub fn get_firmware_rollback_version() -> SysinfoRollbackVersionResponse {
tpm_nv_read!(FIRMWARE_ROLLBACK_NV_INDEX, 0x6, |buf: &[u8]| {
u32::from_le_bytes(buf[0x2..0x6].try_into().unwrap())
})
}
/// Fetch the firmware management parameters from the TPM.
/// Returns u32::MAX (0xFFFFFFFF) on error.
#[cfg(feature = "tlcl")]
pub fn get_firmware_management_parameters() -> SysinfoRollbackVersionResponse {
tpm_nv_read!(
FIRMWARE_MANAGEMENT_PARAMETERS_NV_INDEX,
0x8,
|buf: &[u8]| { u32::from_le_bytes(buf[0x4..0x8].try_into().unwrap()) }
)
}
----- END ./src/sysinfo/rollback.rs -----
----- ./src/sysinfo/stubs.rs -----
#![allow(unused_imports)]
use crate::LOG_DBG;
#[cfg(not(feature = "tlcl"))]
/// This is a stub for whenever Tlcl isn't enabled. Due to the fact that
/// the kernel rollback version is in the TPM but we can't fetch from the
/// TPM without the Tlcl library, we just stub it here and return u32::MAX
// (0xFFFFFFFF)
pub fn get_kernel_rollback_version() -> u32 {
LOG_DBG!("tlcl feature not enabled");
u32::MAX
}
#[cfg(not(feature = "tlcl"))]
/// This is a stub for whenever Tlcl isn't enabled. Due to the fact that
/// the firmware rollback version is in the TPM but we can't fetch from the
/// TPM without the Tlcl library, we just stub it here and return u32::MAX
// (0xFFFFFFFF)
pub fn get_firmware_rollback_version() -> u32 {
LOG_DBG!("tlcl feature not enabled");
u32::MAX
}
#[cfg(not(feature = "tlcl"))]
/// This is a stub for whenever Tlcl isn't enabled. Due to the fact that
/// the firmware management parameters is in the TPM but we can't fetch from the
/// TPM without the Tlcl library, we just stub it here and return u32::MAX
// (0xFFFFFFFF)
pub fn get_firmware_management_parameters() -> u32 {
LOG_DBG!("tlcl feature not enabled");
u32::MAX
}
----- END ./src/sysinfo/stubs.rs -----
----- ./src/sysinfo/backend.rs -----
#[cfg(feature = "tlcl")]
use crate::tlcl::TlclRead;
use crate::{LOG_FATAL, keys, kv_erase, kv_set};
/// Macro to reduce boilerplate
#[cfg(feature = "tlcl")]
#[macro_export]
macro_rules! tpm_nv_read {
($nv_index:expr, $size:expr, $parse_func:expr) => {{
let mut outbuf: [u8; $size] = unsafe { core::mem::zeroed() };
let rc = TlclRead(
$nv_index,
outbuf.as_mut_ptr() as *mut core::ffi::c_void,
$size as u32,
);
if rc != crate::tlcl::constants::TPM_SUCCESS {
LOG_DBG!(
"TlclRead(0x{:X}, outbuf, 0x{:X}) failed with code 0x{:X}",
$nv_index,
$size,
rc
);
return crate::structs::SysinfoRollbackVersionResponse {
rc,
rollback_version: u32::MAX,
};
}
if outbuf.len() < $size {
LOG_DBG!(
"TlclRead(0x{:x}, outbuf, 0x{:x}) returned too few bytes (expected 0x{:x}, got 0x{:x})",
$nv_index,
$size,
$size,
outbuf.len()
);
return crate::structs::SysinfoRollbackVersionResponse {
rc: u32::MAX,
rollback_version: u32::MAX,
};
}
crate::structs::SysinfoRollbackVersionResponse {
rc: rc,
rollback_version: $parse_func(&outbuf),
}
}};
}
const TPM_DEVICES: &[&str] = &[
"/dev/tpm0", /* Cr50 & Ti50 */
"/dev/gsc0", /* Acropora */
"/dev/citadel0", /* Nugget-OS */
];
/// Opens a global file handle to the GSC device.
/// Tries each device in priority order and stores the first
/// successful handle in the GSC_SOCKET key.
pub fn open_gsc_socket() {
for path in TPM_DEVICES {
if let Ok(f) = std::fs::OpenOptions::new()
.read(true)
.write(true)
.open(path)
{
kv_set(keys::GSC_SOCKET, f);
return;
}
}
LOG_FATAL!("failed to open any GSC device");
}
/// Closes the global GSC file handle by erasing it from the store.
/// The File is dropped when the key is removed, which closes the fd.
pub fn close_gsc_socket() {
kv_erase(keys::GSC_SOCKET);
}
----- END ./src/sysinfo/backend.rs -----
----- ./src/crypto.rs -----
/// CRC-32 (IEEE).
pub fn crc32(data: &[u8]) -> u32 {
let mut crc: u32 = 0xFFFFFFFF;
for &byte in data {
let mut b = byte as u32;
for _ in 0..8 {
if (crc ^ b) & 1 == 1 {
crc = (crc >> 1) ^ 0xEDB88320;
} else {
crc >>= 1;
}
b >>= 1;
}
}
!crc
}
/// Adler-32.
pub fn adler32(data: &[u8]) -> u32 {
const MOD: u32 = 65521;
let mut a: u32 = 1;
let mut b: u32 = 0;
for &byte in data {
a = (a + byte as u32) % MOD;
b = (b + a) % MOD;
}
(b << 16) | a
}
/// SHA-1 digest.
pub fn sha1(data: &[u8]) -> [u8; 20] {
fn rot_left(x: u32, n: u32) -> u32 {
(x << n) | (x >> (32 - n))
}
let mut h = [
0x67452301u32,
0xEFCDAB89,
0x98BADCFE,
0x10325476,
0xC3D2E1F0,
];
let mut msg = data.to_vec();
let bit_len = (data.len() as u64) * 8;
msg.push(0x80);
while (msg.len() % 64) != 56 {
msg.push(0);
}
msg.extend_from_slice(&bit_len.to_be_bytes());
for chunk in msg.chunks_exact(64) {
let mut w = [0u32; 80];
for i in 0..16 {
w[i] = u32::from_be_bytes([
chunk[i * 4],
chunk[i * 4 + 1],
chunk[i * 4 + 2],
chunk[i * 4 + 3],
]);
}
for i in 16..80 {
w[i] = rot_left(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
}
let (mut a, mut b, mut c, mut d, mut e) = (h[0], h[1], h[2], h[3], h[4]);
for i in 0..80 {
let (f, k) = match i {
0..=19 => ((b & c) | (!b & d), 0x5A827999),
20..=39 => (b ^ c ^ d, 0x6ED9EBA1),
40..=59 => ((b & c) | (b & d) | (c & d), 0x8F1BBCDC),
_ => (b ^ c ^ d, 0xCA62C1D6),
};
let temp = rot_left(a, 5)
.wrapping_add(f)
.wrapping_add(e)
.wrapping_add(k)
.wrapping_add(w[i]);
e = d;
d = c;
c = rot_left(b, 30);
b = a;
a = temp;
}
h[0] = h[0].wrapping_add(a);
h[1] = h[1].wrapping_add(b);
h[2] = h[2].wrapping_add(c);
h[3] = h[3].wrapping_add(d);
h[4] = h[4].wrapping_add(e);
}
let mut out = [0u8; 20];
for (i, &v) in h.iter().enumerate() {
out[i * 4..(i + 1) * 4].copy_from_slice(&v.to_be_bytes());
}
out
}
/// SHA-256 digest.
pub fn sha256(data: &[u8]) -> [u8; 32] {
const K: [u32; 64] = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
];
fn rot_right(x: u32, n: u32) -> u32 {
(x >> n) | (x << (32 - n))
}
let mut h = [
0x6a09e667u32,
0xbb67ae85,
0x3c6ef372,
0xa54ff53a,
0x510e527f,
0x9b05688c,
0x1f83d9ab,
0x5be0cd19,
];
let mut msg = data.to_vec();
let bit_len = (data.len() as u64) * 8;
msg.push(0x80);
while (msg.len() % 64) != 56 {
msg.push(0);
}
msg.extend_from_slice(&bit_len.to_be_bytes());
for chunk in msg.chunks_exact(64) {
let mut w = [0u32; 64];
for i in 0..16 {
w[i] = u32::from_be_bytes([
chunk[i * 4],
chunk[i * 4 + 1],
chunk[i * 4 + 2],
chunk[i * 4 + 3],
]);
}
for i in 16..64 {
let s0 = rot_right(w[i - 15], 7) ^ rot_right(w[i - 15], 18) ^ (w[i - 15] >> 3);
let s1 = rot_right(w[i - 2], 17) ^ rot_right(w[i - 2], 19) ^ (w[i - 2] >> 10);
w[i] = w[i - 16]
.wrapping_add(s0)
.wrapping_add(w[i - 7])
.wrapping_add(s1);
}
let (mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h_var) =
(h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]);
for i in 0..64 {
let s1 = rot_right(e, 6) ^ rot_right(e, 11) ^ rot_right(e, 25);
let ch = (e & f) ^ (!e & g);
let temp1 = h_var
.wrapping_add(s1)
.wrapping_add(ch)
.wrapping_add(K[i])
.wrapping_add(w[i]);
let s0 = rot_right(a, 2) ^ rot_right(a, 13) ^ rot_right(a, 22);
let maj = (a & b) ^ (a & c) ^ (b & c);
let temp2 = s0.wrapping_add(maj);
h_var = g;
g = f;
f = e;
e = d.wrapping_add(temp1);
d = c;
c = b;
b = a;
a = temp1.wrapping_add(temp2);
}
h[0] = h[0].wrapping_add(a);
h[1] = h[1].wrapping_add(b);
h[2] = h[2].wrapping_add(c);
h[3] = h[3].wrapping_add(d);
h[4] = h[4].wrapping_add(e);
h[5] = h[5].wrapping_add(f);
h[6] = h[6].wrapping_add(g);
h[7] = h[7].wrapping_add(h_var);
}
let mut out = [0u8; 32];
for (i, &v) in h.iter().enumerate() {
out[i * 4..(i + 1) * 4].copy_from_slice(&v.to_be_bytes());
}
out
}
/// HMAC-SHA256.
pub fn hmac_sha256(key: &[u8], data: &[u8]) -> [u8; 32] {
const BLOCK_SIZE: usize = 64;
let mut key_block = [0u8; BLOCK_SIZE];
if key.len() > BLOCK_SIZE {
key_block[..32].copy_from_slice(&sha256(key));
} else {
key_block[..key.len()].copy_from_slice(key);
}
let mut ipad = [0x36u8; BLOCK_SIZE];
let mut opad = [0x5cu8; BLOCK_SIZE];
for i in 0..BLOCK_SIZE {
ipad[i] ^= key_block[i];
opad[i] ^= key_block[i];
}
let mut inner = ipad.to_vec();
inner.extend_from_slice(data);
let inner_hash = sha256(&inner);
let mut outer = opad.to_vec();
outer.extend_from_slice(&inner_hash);
sha256(&outer)
}
/// PBKDF2-HMAC-SHA256 key derivation.
pub fn pbkdf2_hmac_sha256(
password: &[u8],
salt: &[u8],
iterations: u32,
output_len: usize,
) -> Vec<u8> {
let mut result = Vec::with_capacity(output_len);
let mut block_num = 1u32;
while result.len() < output_len {
let mut salt_block = salt.to_vec();
salt_block.extend_from_slice(&block_num.to_be_bytes());
let mut u = hmac_sha256(password, &salt_block);
let mut f = u;
for _ in 1..iterations {
u = hmac_sha256(password, &u);
for i in 0..32 {
f[i] ^= u[i];
}
}
result.extend_from_slice(&f);
block_num += 1;
}
result.truncate(output_len);
result
}
----- END ./src/crypto.rs -----
----- ./src/libargs.rs -----
#![allow(dead_code)]
use std::{collections::HashMap, env};
pub struct ArgCheck {
pub args: Vec<String>,
program_name: String,
description: String,
flags: HashMap<String, FlagInfo>,
help_requested: bool,
}
struct FlagInfo {
long: String,
short: String,
description: String,
takes_value: bool,
}
impl ArgCheck {
/// Build parser from process args.
pub fn new() -> Self {
let args: Vec<String> = env::args().collect();
let program_name = args
.get(0)
.and_then(|p| std::path::Path::new(p).file_name())
.and_then(|n| n.to_str())
.unwrap_or("program")
.to_string();
let help_requested = args.iter().any(|arg| arg == "--help" || arg == "-h");
ArgCheck {
args,
program_name,
description: String::new(),
flags: HashMap::new(),
help_requested,
}
}
/// Set help description.
pub fn set_description(&mut self, desc: &str) -> &mut Self {
self.description = desc.to_string();
self
}
/// Show help and exit when --help or -h is set.
pub fn check_help(&self) {
if self.help_requested {
self.show_help();
}
}
/// Print help text and exit 0.
pub fn show_help(&self) -> ! {
println!("Usage: {} [OPTIONS]", self.program_name);
if !self.description.is_empty() {
println!("\n{}", self.description);
}
if !self.flags.is_empty() {
println!("\nOPTIONS:");
let mut flags: Vec<_> = self.flags.values().collect();
flags.sort_by(|a, b| a.long.cmp(&b.long));
let max_flag_width = flags
.iter()
.map(|flag| {
let short_len = if !flag.short.is_empty() {
flag.short.len() + 2
} else {
0
}; /* +2 for ", " */
let long_len = flag.long.len();
let value_len = if flag.takes_value { 8 } else { 0 }; /* " <VALUE>" */
short_len + long_len + value_len
})
.max()
.unwrap_or(0)
+ 10; /* +10 spaces padding */
for flag in flags {
let short_part = if !flag.short.is_empty() {
format!("{}, ", flag.short)
} else {
" ".to_string()
};
let value_part = if flag.takes_value { " <VALUE>" } else { "" };
let flag_part = format!("{}{}{}", short_part, flag.long, value_part);
println!(
" {:<width$} {}",
flag_part,
flag.description,
width = max_flag_width
);
}
println!(
" {:<width$} {}",
"-h, --help",
"Show this help message",
width = max_flag_width
);
} else {
println!("\nOPTIONS:");
println!(
" {:<width$} {}",
"-h, --help",
"Show this help message",
width = 20
);
}
std::process::exit(0);
}
fn register_flag(&mut self, long: &str, short: &str, desc: &str, takes_value: bool) {
if !self.flags.contains_key(long) {
self.flags.insert(
long.to_string(),
FlagInfo {
long: long.to_string(),
short: short.to_string(),
description: desc.to_string(),
takes_value,
},
);
}
}
/// Get flag value.
/// Supports --flag value and --flag=value.
pub fn fequals(&mut self, arg: &str, shorthand: &str, desc: &str) -> Option<String> {
self.register_flag(arg, shorthand, desc, true);
for i in 0..self.args.len() {
let item = &self.args[i];
if item.starts_with(arg) || (!shorthand.is_empty() && item.starts_with(shorthand)) {
if let Some(index) = item.find('=') {
if &item[..index] == arg || &item[..index] == shorthand {
return Some(item[index + 1..].to_string());
}
}
}
if item == arg || (!shorthand.is_empty() && item == shorthand) {
return self.args.get(i + 1).cloned();
}
}
None
}
/// True when the flag is present.
pub fn fbool(&mut self, arg: &str, shorthand: &str, desc: &str) -> bool {
self.register_flag(arg, shorthand, desc, false);
self
.args
.iter()
.any(|item| item == arg || item == shorthand)
}
/// Same as fequals, but empty string on miss.
pub fn fequals_str(&mut self, arg: &str, shorthand: &str, desc: &str) -> String {
self.fequals(arg, shorthand, desc).unwrap_or_default()
}
}
impl Drop for ArgCheck {
fn drop(&mut self) {
self.check_help();
}
}
----- END ./src/libargs.rs -----
----- ./src/tlcl/v2_0.rs -----
use crate::tlcl::tpm20::constants::nv_read_public_response;
pub struct Client;
impl Client {
pub fn new() -> Self {
Self
}
crate::tlcl::client::impl_tlcl_client_common!(tpm20);
pub fn nv_read_public(&self, index: u32, presp: *mut nv_read_public_response) -> u32 {
crate::tlcl::commands::tpm20::TlclNVReadPublic(index, presp as *mut core::ffi::c_void)
}
}
----- END ./src/tlcl/v2_0.rs -----
----- ./src/tlcl/constants.rs -----
/* Copyright 2013 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
pub const TPM_SUCCESS: u32 = 0x00000000;
pub const TPM_E_ALREADY_INITIALIZED: u32 = 0x00005000; /* vboot local */
pub const TPM_E_INTERNAL_INCONSISTENCY: u32 = 0x00005001; /* vboot local */
pub const TPM_E_MUST_REBOOT: u32 = 0x00005002; /* vboot local */
pub const TPM_E_CORRUPTED_STATE: u32 = 0x00005003; /* vboot local */
pub const TPM_E_COMMUNICATION_ERROR: u32 = 0x00005004; /* vboot local */
pub const TPM_E_RESPONSE_TOO_LARGE: u32 = 0x00005005; /* vboot local */
pub const TPM_E_NO_DEVICE: u32 = 0x00005006; /* vboot local */
pub const TPM_E_INPUT_TOO_SMALL: u32 = 0x00005007; /* vboot local */
pub const TPM_E_WRITE_FAILURE: u32 = 0x00005008; /* vboot local */
pub const TPM_E_READ_EMPTY: u32 = 0x00005009; /* vboot local */
pub const TPM_E_READ_FAILURE: u32 = 0x0000500a; /* vboot local */
pub const TPM_E_STRUCT_SIZE: u32 = 0x0000500b; /* vboot local */
pub const TPM_E_STRUCT_VERSION: u32 = 0x0000500c; /* vboot local */
pub const TPM_E_INTERNAL_ERROR: u32 = 0x0000500d; /* vboot local */
pub const TPM_E_INVALID_RESPONSE: u32 = 0x0000500e; /* vboot local */
pub const TPM_E_BUFFER_SIZE: u32 = 0x0000500f; /* vboot local */
pub const TPM_E_NO_SUCH_COMMAND: u32 = 0x00005010; /* vboot local */
/*
* AP firmware relies on Tlcl returning these exact TPM1.2 error codes
* regardless of the TPM spec version in certain sitautions. So, TPM2.0 should
* map to these errors when necessary. All TPM2.0-spec-defined errors have
* either 0x100 or 0x80 bit set, so there is no confusion with actual error
* codes returned from a TPM2.0 chip.
*/
pub const TPM_E_AUTHFAIL: u32 = 0x00000001;
pub const TPM_E_BADINDEX: u32 = 0x00000002;
pub const TPM_E_BAD_ORDINAL: u32 = 0x0000000a;
pub const TPM_E_OWNER_SET: u32 = 0x00000014;
pub const TPM_E_BADTAG: u32 = 0x0000001e;
pub const TPM_E_IOERROR: u32 = 0x0000001f;
pub const TPM_E_INVALID_POSTINIT: u32 = 0x00000026;
pub const TPM_E_BAD_PRESENCE: u32 = 0x0000002d;
pub const TPM_E_AREA_LOCKED: u32 = 0x0000003c;
pub const TPM_E_MAXNVWRITES: u32 = 0x00000048;
pub const TPM_E_NON_FATAL: u32 = 0x800;
pub const TPM_E_NEEDS_SELFTEST: u32 = TPM_E_NON_FATAL + 1;
pub const TPM_E_DOING_SELFTEST: u32 = TPM_E_NON_FATAL + 2;
----- END ./src/tlcl/constants.rs -----
----- ./src/tlcl/v1_2.rs -----
pub struct Client;
impl Client {
pub fn new() -> Self {
Self
}
crate::tlcl::client::impl_tlcl_client_common!(tpm12);
pub fn nv_read_public(&self, index: u32, presp: *mut core::ffi::c_void) -> u32 {
crate::tlcl::commands::tpm12::TlclNVReadPublic(index, presp)
}
}
----- END ./src/tlcl/v1_2.rs -----
----- ./src/tlcl/client.rs -----
macro_rules! impl_tlcl_client_common {
($module:ident) => {
pub fn force_clear(&self) -> u32 {
crate::tlcl::commands::$module::TlclForceClear()
}
pub fn define_space(&self, index: u32, perm: u32, size: u32) -> u32 {
crate::tlcl::commands::$module::TlclDefineSpace(index, perm, size)
}
pub fn undefine_space(&self, index: u32) -> u32 {
crate::tlcl::commands::$module::TlclUndefineSpace(index)
}
pub fn undefine_space_ex(
&self,
owner_auth: *const u8,
owner_auth_size: u32,
index: u32,
) -> u32 {
crate::tlcl::commands::$module::TlclUndefineSpaceEx(owner_auth, owner_auth_size, index)
}
pub fn define_space_ex(
&self,
owner_auth: *const u8,
owner_auth_size: u32,
index: u32,
perm: u32,
size: u32,
auth_policy: *const core::ffi::c_void,
auth_policy_size: u32,
) -> u32 {
crate::tlcl::commands::$module::TlclDefineSpaceEx(
owner_auth,
owner_auth_size,
index,
perm,
size,
auth_policy,
auth_policy_size,
)
}
pub fn init_nv_auth_policy(
&self,
pcr_selection_bitmap: u32,
pcr_values: *const u8,
auth_policy: *mut core::ffi::c_void,
auth_policy_size: &mut u32,
) -> u32 {
crate::tlcl::commands::$module::TlclInitNvAuthPolicy(
pcr_selection_bitmap,
pcr_values,
auth_policy,
auth_policy_size,
)
}
pub fn get_space_info(
&self,
index: u32,
attributes: &mut u32,
size: &mut u32,
auth_policy: *mut core::ffi::c_void,
auth_policy_size: &mut u32,
) -> u32 {
crate::tlcl::commands::$module::TlclGetSpaceInfo(
index,
attributes,
size,
auth_policy,
auth_policy_size,
)
}
pub fn get_permissions(&self, index: u32, permissions: &mut u32) -> u32 {
crate::tlcl::commands::$module::TlclGetPermissions(index, permissions)
}
pub fn physical_presence_cmd_enable(&self) -> u32 {
crate::tlcl::commands::$module::TlclPhysicalPresenceCMDEnable()
}
pub fn assert_physical_presence(&self) -> u32 {
crate::tlcl::commands::$module::TlclAssertPhysicalPresence()
}
pub fn set_enable(&self) -> u32 {
crate::tlcl::commands::$module::TlclSetEnable()
}
pub fn read(&self, index: u32, outbuf: *mut core::ffi::c_void, length: u32) -> u32 {
crate::tlcl::commands::$module::TlclRead(index, outbuf, length)
}
pub fn read_with_offset(
&self,
index: u32,
size: u32,
offset: u32,
outbuf: *mut core::ffi::c_void,
) -> u32 {
crate::tlcl::commands::$module::TlclReadWithOffset(index, size, offset, outbuf)
}
pub fn startup(&self) -> u32 {
crate::tlcl::commands::$module::TlclStartup()
}
pub fn save_state(&self) -> u32 {
crate::tlcl::commands::$module::TlclSaveState()
}
pub fn resume(&self) -> u32 {
crate::tlcl::commands::$module::TlclResume()
}
pub fn write(&self, index: u32, data: *const core::ffi::c_void, length: u32) -> u32 {
crate::tlcl::commands::$module::TlclWrite(index, data, length)
}
pub fn write_with_offset(
&self,
index: u32,
data: *const core::ffi::c_void,
length: u32,
offset: u32,
) -> u32 {
crate::tlcl::commands::$module::TlclWriteWithOffset(index, data, length, offset)
}
};
}
pub(crate) use impl_tlcl_client_common;
----- END ./src/tlcl/client.rs -----
----- ./src/tlcl/mod.rs -----
#![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()
}
----- END ./src/tlcl/mod.rs -----
----- ./src/tlcl/exports.rs -----
macro_rules! DEFINE_TLCL_EXPORT {
($export_name:ident, $internal:path) => {
pub use $internal as $export_name;
};
}
DEFINE_TLCL_EXPORT!(TlclForceClear, crate::tlcl::commands::TlclForceClear);
DEFINE_TLCL_EXPORT!(TlclDefineSpace, crate::tlcl::commands::TlclDefineSpace);
DEFINE_TLCL_EXPORT!(TlclUndefineSpace, crate::tlcl::commands::TlclUndefineSpace);
DEFINE_TLCL_EXPORT!(
TlclUndefineSpaceEx,
crate::tlcl::commands::TlclUndefineSpaceEx
);
DEFINE_TLCL_EXPORT!(TlclDefineSpaceEx, crate::tlcl::commands::TlclDefineSpaceEx);
DEFINE_TLCL_EXPORT!(
TlclInitNvAuthPolicy,
crate::tlcl::commands::TlclInitNvAuthPolicy
);
DEFINE_TLCL_EXPORT!(TlclGetSpaceInfo, crate::tlcl::commands::TlclGetSpaceInfo);
DEFINE_TLCL_EXPORT!(
TlclGetPermissions,
crate::tlcl::commands::TlclGetPermissions
);
DEFINE_TLCL_EXPORT!(
TlclPhysicalPresenceCMDEnable,
crate::tlcl::commands::TlclPhysicalPresenceCMDEnable
);
DEFINE_TLCL_EXPORT!(
TlclAssertPhysicalPresence,
crate::tlcl::commands::TlclAssertPhysicalPresence
);
DEFINE_TLCL_EXPORT!(TlclSetEnable, crate::tlcl::commands::TlclSetEnable);
DEFINE_TLCL_EXPORT!(TlclRead, crate::tlcl::commands::TlclRead);
DEFINE_TLCL_EXPORT!(
TlclReadWithOffset,
crate::tlcl::commands::TlclReadWithOffset
);
DEFINE_TLCL_EXPORT!(TlclNVReadPublic, crate::tlcl::commands::TlclNVReadPublic);
DEFINE_TLCL_EXPORT!(TlclStartup, crate::tlcl::commands::TlclStartup);
DEFINE_TLCL_EXPORT!(TlclSaveState, crate::tlcl::commands::TlclSaveState);
DEFINE_TLCL_EXPORT!(TlclResume, crate::tlcl::commands::TlclResume);
DEFINE_TLCL_EXPORT!(TlclWrite, crate::tlcl::commands::TlclWrite);
DEFINE_TLCL_EXPORT!(
TlclWriteWithOffset,
crate::tlcl::commands::TlclWriteWithOffset
);
----- END ./src/tlcl/exports.rs -----
----- ./src/tlcl/commands/mod.rs -----
#[cfg(feature = "tpm1_2")]
pub mod tpm12;
#[cfg(all(feature = "tpm1_2", not(feature = "tpm2_0")))]
pub use tpm12::*;
#[cfg(feature = "tpm2_0")]
pub mod tpm20;
#[cfg(feature = "tpm2_0")]
pub use tpm20::*;
#[cfg(all(not(feature = "tpm1_2"), not(feature = "tpm2_0")))]
pub mod stubs;
#[cfg(all(not(feature = "tpm1_2"), not(feature = "tpm2_0")))]
pub use stubs::*;
----- END ./src/tlcl/commands/mod.rs -----
----- ./src/tlcl/commands/tpm20/define.rs -----
#![allow(non_snake_case)]
use crate::tlcl::{
commands::tpm20::TlclNVReadPublic,
constants::{TPM_E_BUFFER_SIZE, TPM_SUCCESS},
tpm20::{
constants::{
HR_NV_INDEX, TPM_ALG_SHA256, TPM2_NV_DefineSpace, TPM2_NV_UndefineSpace, TPMA_NV_AUTHREAD,
TPMA_NV_AUTHWRITE, TPMA_NV_MASK_READ, TPMA_NV_MASK_WRITE, TPMA_NV_PLATFORMCREATE,
nv_read_public_response, tpm2_nv_define_space_cmd, tpm2_nv_undefine_space_cmd,
},
tpm_get_response_code,
},
};
pub fn TlclGetPermissions(index: u32, permissions: &mut u32) -> u32 {
let mut pub_resp: nv_read_public_response = unsafe { core::mem::zeroed() };
let rv = TlclNVReadPublic(index, &mut pub_resp as *mut _ as *mut core::ffi::c_void);
if rv != TPM_SUCCESS {
return rv;
}
*permissions =
unsafe { core::ptr::read_unaligned(core::ptr::addr_of!(pub_resp.nvPublic.attributes)) };
TPM_SUCCESS
}
pub fn TlclDefineSpaceEx(
owner_auth: *const u8,
owner_auth_size: u32,
index: u32,
perm: u32,
size: u32,
auth_policy: *const core::ffi::c_void,
auth_policy_size: u32,
) -> u32 {
let mut define_space: tpm2_nv_define_space_cmd = unsafe { core::mem::zeroed() };
let mut perm = perm;
if (perm & TPMA_NV_MASK_WRITE) == 0 {
perm |= TPMA_NV_AUTHWRITE;
}
if (perm & TPMA_NV_MASK_READ) == 0 {
perm |= TPMA_NV_AUTHREAD;
}
define_space.publicInfo.nvIndex = HR_NV_INDEX + index;
define_space.publicInfo.dataSize = size as u16;
define_space.publicInfo.attributes = perm;
define_space.publicInfo.nameAlg = TPM_ALG_SHA256;
if !owner_auth.is_null() && owner_auth_size > 0 {
define_space.auth.size = owner_auth_size as u16;
define_space.auth.buffer = owner_auth;
}
if !auth_policy.is_null() && auth_policy_size > 0 {
define_space.publicInfo.authPolicy.size = auth_policy_size as u16;
define_space.publicInfo.authPolicy.buffer = auth_policy as *const u8;
}
tpm_get_response_code(
TPM2_NV_DefineSpace,
&define_space as *const tpm2_nv_define_space_cmd as *const core::ffi::c_void,
)
}
pub fn TlclUndefineSpaceEx(_owner_auth: *const u8, _owner_auth_size: u32, index: u32) -> u32 {
let mut undefine_space: tpm2_nv_undefine_space_cmd = unsafe { core::mem::zeroed() };
let mut permissions: u32 = 0;
let rv: u32;
rv = TlclGetPermissions(index, &mut permissions);
if rv != TPM_SUCCESS {
return rv;
}
undefine_space.nvIndex = HR_NV_INDEX + index;
undefine_space.use_platform_auth = ((permissions & TPMA_NV_PLATFORMCREATE) > 0) as u8;
tpm_get_response_code(
TPM2_NV_UndefineSpace,
&undefine_space as *const tpm2_nv_undefine_space_cmd as *const core::ffi::c_void,
)
}
pub fn TlclDefineSpace(index: u32, perm: u32, size: u32) -> u32 {
TlclDefineSpaceEx(
core::ptr::null(),
0,
index,
perm,
size,
core::ptr::null(),
0,
)
}
pub fn TlclUndefineSpace(index: u32) -> u32 {
TlclUndefineSpaceEx(core::ptr::null(), 0, index)
}
pub fn TlclInitNvAuthPolicy(
_pcr_selection_bitmap: u32,
_pcr_values: *const u8,
_auth_policy: *mut core::ffi::c_void,
auth_policy_size: &mut u32,
) -> u32 {
*auth_policy_size = 0;
TPM_SUCCESS
}
pub fn TlclGetSpaceInfo(
index: u32,
attributes: &mut u32,
size: &mut u32,
auth_policy: *mut core::ffi::c_void,
auth_policy_size: &mut u32,
) -> u32 {
let mut resp: nv_read_public_response = unsafe { core::mem::zeroed() };
let rv = TlclNVReadPublic(index, &mut resp as *mut _ as *mut core::ffi::c_void);
if rv != TPM_SUCCESS {
return rv;
}
*attributes = unsafe { core::ptr::read_unaligned(core::ptr::addr_of!(resp.nvPublic.attributes)) };
*size = unsafe { core::ptr::read_unaligned(core::ptr::addr_of!(resp.nvPublic.dataSize)) } as u32;
let policy_size =
unsafe { core::ptr::read_unaligned(core::ptr::addr_of!(resp.nvPublic.authPolicy.size)) } as u32;
if policy_size > *auth_policy_size {
return TPM_E_BUFFER_SIZE;
}
if policy_size > 0 && auth_policy.is_null() {
return TPM_E_BUFFER_SIZE;
}
*auth_policy_size = policy_size;
if policy_size > 0 {
unsafe {
core::ptr::copy_nonoverlapping(
resp.nvPublic.authPolicy.buffer,
auth_policy as *mut u8,
policy_size as usize,
);
}
}
TPM_SUCCESS
}
----- END ./src/tlcl/commands/tpm20/define.rs -----
----- ./src/tlcl/commands/tpm20/write.rs -----
#![allow(non_snake_case)]
use crate::tlcl::tpm20::{
constants::{HR_NV_INDEX, TPM2_NV_Write, TPM2B, tpm2_nv_write_cmd},
tpm_get_response_code,
};
pub fn TlclWrite(index: u32, data: *const core::ffi::c_void, length: u32) -> u32 {
TlclWriteWithOffset(index, data, length, 0)
}
pub fn TlclWriteWithOffset(
index: u32,
data: *const core::ffi::c_void,
length: u32,
offset: u32,
) -> u32 {
let nv_writeData = TPM2B {
size: length as u16,
buffer: data as *const u8,
};
let nv_writec = tpm2_nv_write_cmd {
nvIndex: HR_NV_INDEX + index,
data: nv_writeData,
offset: offset.try_into().unwrap(),
};
return tpm_get_response_code(
TPM2_NV_Write,
&nv_writec as *const tpm2_nv_write_cmd as *const core::ffi::c_void,
);
}
----- END ./src/tlcl/commands/tpm20/write.rs -----
----- ./src/tlcl/commands/tpm20/clear.rs -----
#![allow(non_snake_case)]
use crate::tlcl::tpm20::{constants::TPM2_Clear, tpm_get_response_code};
pub fn TlclForceClear() -> u32 {
tpm_get_response_code(TPM2_Clear, std::ptr::null())
}
----- END ./src/tlcl/commands/tpm20/clear.rs -----
----- ./src/tlcl/commands/tpm20/read.rs -----
#![allow(non_snake_case)]
use crate::tlcl::{
constants::{TPM_E_BADINDEX, TPM_E_READ_EMPTY, TPM_E_RESPONSE_TOO_LARGE, TPM_SUCCESS},
tpm20::{
constants::{
HR_NV_INDEX, TPM2_NV_Read, TPM2_NV_ReadPublic, nv_read_public_response, tpm2_nv_read_cmd,
tpm2_nv_read_public_cmd, tpm2_response,
},
tpm_send_receive,
},
};
pub fn TlclRead(index: u32, outbuf: *mut core::ffi::c_void, length: u32) -> u32 {
TlclReadWithOffset(index, length, 0, outbuf)
}
pub fn TlclReadWithOffset(
index: u32,
length: u32,
offset: u32,
outbuf: *mut core::ffi::c_void,
) -> u32 {
let mut nv_readc: tpm2_nv_read_cmd = unsafe { core::mem::zeroed() };
let mut response: tpm2_response = unsafe { core::mem::zeroed() };
let rv: u32;
nv_readc.nvIndex = HR_NV_INDEX + index;
nv_readc.size = length as u16;
nv_readc.offset = offset as u16;
rv = tpm_send_receive(
TPM2_NV_Read,
&nv_readc as *const tpm2_nv_read_cmd as *const core::ffi::c_void,
&mut response,
);
match rv {
TPM_SUCCESS => {}
0x14a | 0x28b => {
return TPM_E_BADINDEX;
}
_ => {
return rv;
}
}
let nvr = unsafe { &response.body.nvr };
if length > nvr.buffer.size as u32 {
return TPM_E_RESPONSE_TOO_LARGE;
}
if length < nvr.buffer.size as u32 {
return TPM_E_READ_EMPTY;
}
unsafe {
core::ptr::copy_nonoverlapping(nvr.buffer.buffer, outbuf as *mut u8, length as usize);
}
TPM_SUCCESS
}
pub fn TlclNVReadPublic(index: u32, presp: *mut core::ffi::c_void) -> u32 {
let mut response: tpm2_response = unsafe { core::mem::zeroed() };
let mut read_pub: tpm2_nv_read_public_cmd = unsafe { core::mem::zeroed() };
let rv: u32;
read_pub.nvIndex = HR_NV_INDEX + index;
rv = tpm_send_receive(
TPM2_NV_ReadPublic,
&read_pub as *const tpm2_nv_read_public_cmd as *const core::ffi::c_void,
&mut response,
);
if rv == TPM_SUCCESS {
unsafe {
core::ptr::copy_nonoverlapping(
&response.body.nv_read_public as *const core::mem::ManuallyDrop<nv_read_public_response>
as *const nv_read_public_response,
presp as *mut nv_read_public_response,
1,
);
}
}
rv
}
----- END ./src/tlcl/commands/tpm20/read.rs -----
----- ./src/tlcl/commands/tpm20/physical_presence.rs -----
#![allow(non_snake_case)]
use crate::tlcl::constants::TPM_SUCCESS;
pub fn TlclPhysicalPresenceCMDEnable() -> u32 {
TPM_SUCCESS
}
pub fn TlclAssertPhysicalPresence() -> u32 {
TPM_SUCCESS
}
pub fn TlclSetEnable() -> u32 {
TPM_SUCCESS
}
----- END ./src/tlcl/commands/tpm20/physical_presence.rs -----
----- ./src/tlcl/commands/tpm20/mod.rs -----
pub mod clear;
pub mod define;
pub mod physical_presence;
pub mod read;
pub mod startup;
pub mod write;
pub use clear::TlclForceClear;
pub use define::{
TlclDefineSpace, TlclDefineSpaceEx, TlclGetPermissions, TlclGetSpaceInfo, TlclInitNvAuthPolicy,
TlclUndefineSpace, TlclUndefineSpaceEx,
};
pub use physical_presence::{
TlclAssertPhysicalPresence, TlclPhysicalPresenceCMDEnable, TlclSetEnable,
};
pub use read::{TlclNVReadPublic, TlclRead, TlclReadWithOffset};
pub use startup::{TlclResume, TlclSaveState, TlclStartup};
pub use write::{TlclWrite, TlclWriteWithOffset};
----- END ./src/tlcl/commands/tpm20/mod.rs -----
----- ./src/tlcl/commands/tpm20/startup.rs -----
#![allow(non_snake_case)]
use crate::tlcl::tpm20::{
constants::{
TPM_SU_CLEAR, TPM_SU_STATE, TPM2_Shutdown, TPM2_Startup, tpm2_shutdown_cmd, tpm2_startup_cmd,
},
tpm_get_response_code,
};
pub fn TlclStartup() -> u32 {
let startup = tpm2_startup_cmd {
startup_type: TPM_SU_CLEAR,
};
return tpm_get_response_code(
TPM2_Startup,
&startup as *const tpm2_startup_cmd as *const core::ffi::c_void,
);
}
pub fn TlclSaveState() -> u32 {
let shutdown = tpm2_shutdown_cmd {
shutdown_type: TPM_SU_STATE,
};
return tpm_get_response_code(
TPM2_Shutdown,
&shutdown as *const tpm2_shutdown_cmd as *const core::ffi::c_void,
);
}
pub fn TlclResume() -> u32 {
let startup = tpm2_startup_cmd {
startup_type: TPM_SU_STATE,
};
return tpm_get_response_code(
TPM2_Startup,
&startup as *const tpm2_startup_cmd as *const core::ffi::c_void,
);
}
----- END ./src/tlcl/commands/tpm20/startup.rs -----
----- ./src/tlcl/commands/tpm12/define.rs -----
#![allow(non_snake_case)]
use crate::tlcl::{
constants::{
TPM_E_AUTHFAIL, TPM_E_BUFFER_SIZE, TPM_E_INVALID_RESPONSE, TPM_E_NO_SUCH_COMMAND,
TPM_E_RESPONSE_TOO_LARGE, TPM_SUCCESS,
},
tpm12::{
constants::{
TPM_AUTH_DATA_LEN, TPM_BUFFER_SIZE, TPM_CAP_NV_INDEX, TPM_NV_AUTH_POLICY,
TPM_ORD_GetCapability, TPM_ORD_NV_DefineSpace, TPM_TAG_RQU_COMMAND, TPM_TAG_RSP_COMMAND,
tpm1_nv_define_space_cmd,
},
tpm_get_response_code,
utils::{
decode_pcr_info, init_default_nv_auth_policy, init_define_space_default_auth_policy,
read_be16, read_be32, write_be16, write_be32,
},
},
vb2ex_tpm_send_recv,
};
pub fn TlclDefineSpace(index: u32, perm: u32, size: u32) -> u32 {
TlclDefineSpaceEx(
core::ptr::null(),
0,
index,
perm,
size,
core::ptr::null(),
0,
)
}
pub fn TlclUndefineSpace(index: u32) -> u32 {
TlclUndefineSpaceEx(core::ptr::null(), 0, index)
}
pub fn TlclUndefineSpaceEx(owner_auth: *const u8, owner_auth_size: u32, index: u32) -> u32 {
TlclDefineSpaceEx(
owner_auth,
owner_auth_size,
index,
0,
0,
core::ptr::null(),
0,
)
}
pub fn TlclDefineSpaceEx(
owner_auth: *const u8,
owner_auth_size: u32,
index: u32,
perm: u32,
size: u32,
auth_policy: *const core::ffi::c_void,
auth_policy_size: u32,
) -> u32 {
if !owner_auth.is_null() || owner_auth_size != 0 {
if owner_auth_size as usize != TPM_AUTH_DATA_LEN {
return TPM_E_AUTHFAIL;
}
return TPM_E_NO_SUCH_COMMAND;
}
if !auth_policy.is_null() && auth_policy_size != core::mem::size_of::<TPM_NV_AUTH_POLICY>() as u32
{
return TPM_E_BUFFER_SIZE;
}
let mut default_policy: TPM_NV_AUTH_POLICY = unsafe { core::mem::zeroed() };
let policy_ref = if auth_policy.is_null() {
init_define_space_default_auth_policy(&mut default_policy);
&default_policy
} else {
unsafe { &*(auth_policy as *const TPM_NV_AUTH_POLICY) }
};
let cmd = tpm1_nv_define_space_cmd {
nvIndex: index,
perm,
size,
auth_policy: policy_ref as *const TPM_NV_AUTH_POLICY,
};
tpm_get_response_code(
TPM_ORD_NV_DefineSpace,
&cmd as *const tpm1_nv_define_space_cmd as *const core::ffi::c_void,
)
}
pub fn TlclInitNvAuthPolicy(
_pcr_selection_bitmap: u32,
_pcr_values: *const u8,
auth_policy: *mut core::ffi::c_void,
auth_policy_size: &mut u32,
) -> u32 {
let required = core::mem::size_of::<TPM_NV_AUTH_POLICY>() as u32;
let provided = *auth_policy_size;
*auth_policy_size = required;
if auth_policy.is_null() || provided < required {
return TPM_E_BUFFER_SIZE;
}
init_default_nv_auth_policy(auth_policy as *mut TPM_NV_AUTH_POLICY);
TPM_SUCCESS
}
pub fn TlclGetSpaceInfo(
index: u32,
attributes: &mut u32,
size: &mut u32,
auth_policy: *mut core::ffi::c_void,
auth_policy_size: &mut u32,
) -> u32 {
let needed_policy_size = core::mem::size_of::<TPM_NV_AUTH_POLICY>() as u32;
let provided_policy_size = *auth_policy_size;
*auth_policy_size = needed_policy_size;
if auth_policy.is_null() || provided_policy_size < needed_policy_size {
return TPM_E_BUFFER_SIZE;
}
let mut cmd = [0u8; 22];
write_be16(cmd.as_mut_ptr(), TPM_TAG_RQU_COMMAND);
write_be32(unsafe { cmd.as_mut_ptr().add(2) }, cmd.len() as u32);
write_be32(unsafe { cmd.as_mut_ptr().add(6) }, TPM_ORD_GetCapability);
write_be32(unsafe { cmd.as_mut_ptr().add(10) }, TPM_CAP_NV_INDEX);
write_be32(
unsafe { cmd.as_mut_ptr().add(14) },
core::mem::size_of::<u32>() as u32,
);
write_be32(unsafe { cmd.as_mut_ptr().add(18) }, index);
let mut response = [0u8; TPM_BUFFER_SIZE];
let mut response_size = TPM_BUFFER_SIZE as u32;
let rv = vb2ex_tpm_send_recv(
cmd.as_ptr(),
cmd.len() as u32,
response.as_mut_ptr(),
&mut response_size,
);
if rv != TPM_SUCCESS {
return rv;
}
if response_size as usize > response.len() {
return TPM_E_RESPONSE_TOO_LARGE;
}
if response_size < 14 {
return TPM_E_INVALID_RESPONSE;
}
if read_be16(response.as_ptr()) != TPM_TAG_RSP_COMMAND {
return TPM_E_INVALID_RESPONSE;
}
let tpm_rc = read_be32(unsafe { response.as_ptr().add(6) });
if tpm_rc != TPM_SUCCESS {
return tpm_rc;
}
let mut cursor = 10usize;
let cap_size = read_be32(unsafe { response.as_ptr().add(cursor) }) as usize;
cursor += 4;
if cap_size > response_size as usize - cursor {
return TPM_E_INVALID_RESPONSE;
}
let end = cursor + cap_size;
if end.saturating_sub(cursor) < 6 {
return TPM_E_INVALID_RESPONSE;
}
cursor += 2;
let response_index = read_be32(unsafe { response.as_ptr().add(cursor) });
cursor += 4;
if response_index != index {
return TPM_E_INVALID_RESPONSE;
}
let policy = auth_policy as *mut TPM_NV_AUTH_POLICY;
let pcr_read = unsafe { core::ptr::addr_of_mut!((*policy).pcr_info_read) };
let pcr_write = unsafe { core::ptr::addr_of_mut!((*policy).pcr_info_write) };
if !decode_pcr_info(&response, &mut cursor, end, pcr_read)
|| !decode_pcr_info(&response, &mut cursor, end, pcr_write)
{
return TPM_E_INVALID_RESPONSE;
}
if end.saturating_sub(cursor) != 13 {
return TPM_E_INVALID_RESPONSE;
}
cursor += 2;
*attributes = read_be32(unsafe { response.as_ptr().add(cursor) });
cursor += 4;
cursor += 3;
*size = read_be32(unsafe { response.as_ptr().add(cursor) });
TPM_SUCCESS
}
pub fn TlclGetPermissions(index: u32, permissions: &mut u32) -> u32 {
let mut dummy_size: u32 = 0;
let mut dummy_policy: TPM_NV_AUTH_POLICY = unsafe { core::mem::zeroed() };
let mut dummy_policy_size: u32 = core::mem::size_of::<TPM_NV_AUTH_POLICY>() as u32;
TlclGetSpaceInfo(
index,
permissions,
&mut dummy_size,
&mut dummy_policy as *mut TPM_NV_AUTH_POLICY as *mut core::ffi::c_void,
&mut dummy_policy_size,
)
}
----- END ./src/tlcl/commands/tpm12/define.rs -----
----- ./src/tlcl/commands/tpm12/write.rs -----
#![allow(non_snake_case)]
use crate::tlcl::tpm12::{
constants::{TPM_ORD_NV_WriteValue, tpm1_nv_write_cmd},
tpm_get_response_code,
};
pub fn TlclWrite(index: u32, data: *const core::ffi::c_void, length: u32) -> u32 {
TlclWriteWithOffset(index, data, length, 0)
}
pub fn TlclWriteWithOffset(
index: u32,
data: *const core::ffi::c_void,
length: u32,
offset: u32,
) -> u32 {
let nv_writec = tpm1_nv_write_cmd {
nvIndex: index,
offset,
size: length,
data: data as *const u8,
};
return tpm_get_response_code(
TPM_ORD_NV_WriteValue,
&nv_writec as *const tpm1_nv_write_cmd as *const core::ffi::c_void,
);
}
----- END ./src/tlcl/commands/tpm12/write.rs -----
----- ./src/tlcl/commands/tpm12/clear.rs -----
#![allow(non_snake_case)]
use crate::tlcl::tpm12::{constants::TPM_ORD_ForceClear, tpm_get_response_code};
pub fn TlclForceClear() -> u32 {
tpm_get_response_code(TPM_ORD_ForceClear, core::ptr::null())
}
----- END ./src/tlcl/commands/tpm12/clear.rs -----
----- ./src/tlcl/commands/tpm12/read.rs -----
#![allow(non_snake_case)]
use crate::tlcl::{
constants::{TPM_E_NO_SUCH_COMMAND, TPM_E_READ_EMPTY, TPM_E_RESPONSE_TOO_LARGE, TPM_SUCCESS},
tpm12::{
constants::{TPM_ORD_NV_ReadValue, tpm1_nv_read_cmd, tpm1_response},
tpm_send_receive,
},
};
pub fn TlclRead(index: u32, outbuf: *mut core::ffi::c_void, length: u32) -> u32 {
TlclReadWithOffset(index, length, 0, outbuf)
}
pub fn TlclReadWithOffset(
index: u32,
length: u32,
offset: u32,
outbuf: *mut core::ffi::c_void,
) -> u32 {
let nv_readc = tpm1_nv_read_cmd {
nvIndex: index,
offset,
size: length,
};
let mut response: tpm1_response = unsafe { core::mem::zeroed() };
let rv = tpm_send_receive(
TPM_ORD_NV_ReadValue,
&nv_readc as *const tpm1_nv_read_cmd as *const core::ffi::c_void,
&mut response,
);
if rv != TPM_SUCCESS {
return rv;
}
if length > response.nvr.data_size {
return TPM_E_RESPONSE_TOO_LARGE;
}
if length < response.nvr.data_size {
return TPM_E_READ_EMPTY;
}
unsafe {
core::ptr::copy_nonoverlapping(
response.nvr.data.as_ptr(),
outbuf as *mut u8,
length as usize,
);
}
TPM_SUCCESS
}
pub fn TlclNVReadPublic(_index: u32, _presp: *mut core::ffi::c_void) -> u32 {
TPM_E_NO_SUCH_COMMAND
}
----- END ./src/tlcl/commands/tpm12/read.rs -----
----- ./src/tlcl/commands/tpm12/physical_presence.rs -----
#![allow(non_snake_case)]
use crate::tlcl::tpm12::{
constants::{
TPM_ORD_PhysicalEnable, TPM_PHYSICAL_PRESENCE_CMD_ENABLE, TPM_PHYSICAL_PRESENCE_PRESENT,
TSC_ORD_PhysicalPresence, tpm1_physical_presence_cmd,
},
tpm_get_response_code,
};
pub fn TlclPhysicalPresenceCMDEnable() -> u32 {
let command_body = tpm1_physical_presence_cmd {
physical_presence: TPM_PHYSICAL_PRESENCE_CMD_ENABLE,
};
tpm_get_response_code(
TSC_ORD_PhysicalPresence,
&command_body as *const tpm1_physical_presence_cmd as *const core::ffi::c_void,
)
}
pub fn TlclAssertPhysicalPresence() -> u32 {
let command_body = tpm1_physical_presence_cmd {
physical_presence: TPM_PHYSICAL_PRESENCE_PRESENT,
};
tpm_get_response_code(
TSC_ORD_PhysicalPresence,
&command_body as *const tpm1_physical_presence_cmd as *const core::ffi::c_void,
)
}
pub fn TlclSetEnable() -> u32 {
tpm_get_response_code(TPM_ORD_PhysicalEnable, core::ptr::null())
}
----- END ./src/tlcl/commands/tpm12/physical_presence.rs -----
----- ./src/tlcl/commands/tpm12/mod.rs -----
pub mod clear;
pub mod define;
pub mod physical_presence;
pub mod read;
pub mod startup;
pub mod write;
pub use clear::TlclForceClear;
pub use define::{
TlclDefineSpace, TlclDefineSpaceEx, TlclGetPermissions, TlclGetSpaceInfo, TlclInitNvAuthPolicy,
TlclUndefineSpace, TlclUndefineSpaceEx,
};
pub use physical_presence::{
TlclAssertPhysicalPresence, TlclPhysicalPresenceCMDEnable, TlclSetEnable,
};
pub use read::{TlclNVReadPublic, TlclRead, TlclReadWithOffset};
pub use startup::{TlclResume, TlclSaveState, TlclStartup};
pub use write::{TlclWrite, TlclWriteWithOffset};
----- END ./src/tlcl/commands/tpm12/mod.rs -----
----- ./src/tlcl/commands/tpm12/startup.rs -----
#![allow(non_snake_case)]
use crate::tlcl::tpm12::{
constants::{TPM_ORD_SaveState, TPM_ORD_Startup, TPM_ST_CLEAR, TPM_ST_STATE, tpm1_startup_cmd},
tpm_get_response_code,
};
pub fn TlclStartup() -> u32 {
let startup = tpm1_startup_cmd {
startup_type: TPM_ST_CLEAR,
};
tpm_get_response_code(
TPM_ORD_Startup,
&startup as *const tpm1_startup_cmd as *const core::ffi::c_void,
)
}
pub fn TlclSaveState() -> u32 {
tpm_get_response_code(TPM_ORD_SaveState, core::ptr::null())
}
pub fn TlclResume() -> u32 {
let startup = tpm1_startup_cmd {
startup_type: TPM_ST_STATE,
};
tpm_get_response_code(
TPM_ORD_Startup,
&startup as *const tpm1_startup_cmd as *const core::ffi::c_void,
)
}
----- END ./src/tlcl/commands/tpm12/startup.rs -----
----- ./src/tlcl/commands/stubs/mod.rs -----
#![allow(non_snake_case)]
fn no_such_command() -> u32 {
crate::tlcl::constants::TPM_E_NO_SUCH_COMMAND
}
pub fn TlclForceClear() -> u32 {
no_such_command()
}
pub fn TlclDefineSpace(_index: u32, _perm: u32, _size: u32) -> u32 {
no_such_command()
}
pub fn TlclUndefineSpace(_index: u32) -> u32 {
no_such_command()
}
pub fn TlclUndefineSpaceEx(_owner_auth: *const u8, _owner_auth_size: u32, _index: u32) -> u32 {
no_such_command()
}
pub fn TlclDefineSpaceEx(
_owner_auth: *const u8,
_owner_auth_size: u32,
_index: u32,
_perm: u32,
_size: u32,
_auth_policy: *const core::ffi::c_void,
_auth_policy_size: u32,
) -> u32 {
no_such_command()
}
pub fn TlclInitNvAuthPolicy(
_pcr_selection_bitmap: u32,
_pcr_values: *const u8,
_auth_policy: *mut core::ffi::c_void,
_auth_policy_size: &mut u32,
) -> u32 {
no_such_command()
}
pub fn TlclGetSpaceInfo(
_index: u32,
_attributes: &mut u32,
_size: &mut u32,
_auth_policy: *mut core::ffi::c_void,
_auth_policy_size: &mut u32,
) -> u32 {
no_such_command()
}
pub fn TlclGetPermissions(_index: u32, _permissions: &mut u32) -> u32 {
no_such_command()
}
pub fn TlclPhysicalPresenceCMDEnable() -> u32 {
no_such_command()
}
pub fn TlclAssertPhysicalPresence() -> u32 {
no_such_command()
}
pub fn TlclSetEnable() -> u32 {
no_such_command()
}
pub fn TlclRead(_index: u32, _outbuf: *mut core::ffi::c_void, _length: u32) -> u32 {
no_such_command()
}
pub fn TlclReadWithOffset(
_index: u32,
_size: u32,
_offset: u32,
_outbuf: *mut core::ffi::c_void,
) -> u32 {
no_such_command()
}
pub fn TlclNVReadPublic(_index: u32, _presp: *mut core::ffi::c_void) -> u32 {
no_such_command()
}
pub fn TlclStartup() -> u32 {
no_such_command()
}
pub fn TlclSaveState() -> u32 {
no_such_command()
}
pub fn TlclResume() -> u32 {
no_such_command()
}
pub fn TlclWrite(_index: u32, _data: *const core::ffi::c_void, _length: u32) -> u32 {
no_such_command()
}
pub fn TlclWriteWithOffset(
_index: u32,
_data: *const core::ffi::c_void,
_length: u32,
_offset: u32,
) -> u32 {
no_such_command()
}
----- END ./src/tlcl/commands/stubs/mod.rs -----
----- ./src/tlcl/backend.rs -----
use crate::{LOG_DBG, tlcl::vb2ex_tpm_send_recv};
pub(crate) fn tpm_get_response<const N: usize, R, MarshalFn, UnmarshalFn, CodeFn>(
command: u32,
command_body: *const core::ffi::c_void,
response: &mut R,
marshal_fn: MarshalFn,
unmarshal_fn: UnmarshalFn,
code_fn: CodeFn,
) -> u32
where
MarshalFn: Fn(u32, *const core::ffi::c_void, &mut [u8; N], usize) -> i32,
UnmarshalFn: Fn(u32, *const core::ffi::c_void, i32, &mut R) -> i32,
CodeFn: Fn(&R) -> u32,
{
let mut cr_buffer = [0u8; N];
let out_size = marshal_fn(command, command_body, &mut cr_buffer, N);
if out_size < 0 {
LOG_DBG!("command 0x{:X}, failed to serialize", command);
return crate::tlcl::constants::TPM_E_WRITE_FAILURE;
}
let mut in_size = N as u32;
let res = vb2ex_tpm_send_recv(
cr_buffer.as_ptr(),
out_size as u32,
cr_buffer.as_mut_ptr(),
&mut in_size,
);
if res != crate::tlcl::constants::TPM_SUCCESS {
LOG_DBG!(
"tpm transaction failed for {:#} with error code {:#}",
command,
res
);
return res;
}
if unmarshal_fn(
command,
cr_buffer.as_ptr() as *const core::ffi::c_void,
in_size as i32,
response,
) < 0
{
LOG_DBG!("command 0x{:X}, failed to parse response", command);
return crate::tlcl::constants::TPM_E_READ_FAILURE;
}
if code_fn(response) != crate::tlcl::constants::TPM_SUCCESS {
LOG_DBG!(
"command 0x{:X} failed! tpm returned 0x{:X}",
command,
code_fn(response)
);
return code_fn(response);
}
LOG_DBG!(
"command 0x{:X}, return code 0x{:X}",
command,
code_fn(response)
);
crate::tlcl::constants::TPM_SUCCESS
}
----- END ./src/tlcl/backend.rs -----
----- ./src/tlcl/bytes.rs -----
pub fn read_be16(src: *const u8) -> u16 {
unsafe { ((*src.add(0) as u16) << 8) | (*src.add(1) as u16) }
}
pub fn read_be32(src: *const u8) -> u32 {
unsafe {
((*src.add(0) as u32) << 24)
| ((*src.add(1) as u32) << 16)
| ((*src.add(2) as u32) << 8)
| (*src.add(3) as u32)
}
}
pub fn write_be16(dest: *mut u8, val: u16) {
unsafe {
*dest.add(0) = (val >> 8) as u8;
*dest.add(1) = val as u8;
}
}
pub fn write_be32(dest: *mut u8, val: u32) {
unsafe {
*dest.add(0) = (val >> 24) as u8;
*dest.add(1) = (val >> 16) as u8;
*dest.add(2) = (val >> 8) as u8;
*dest.add(3) = val as u8;
}
}
----- END ./src/tlcl/bytes.rs -----
----- ./src/tlcl/tpm20/constants.rs -----
/* Copyright 2016 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Some TPM constants and type definitions for standalone compilation for use
* in the firmware
*/
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
pub const TPM_BUFFER_SIZE: usize = 2048;
/* Tpm2 command tags. */
pub const TPM_ST_NO_SESSIONS: u16 = 0x8001;
pub const TPM_ST_SESSIONS: u16 = 0x8002;
/* TPM2 command codes. */
pub const TPM2_EvictControl: TPM_CC = 0x00000120;
pub const TPM2_Hierarchy_Control: TPM_CC = 0x00000121;
pub const TPM2_NV_UndefineSpace: TPM_CC = 0x00000122;
pub const TPM2_Clear: TPM_CC = 0x00000126;
pub const TPM2_NV_DefineSpace: TPM_CC = 0x0000012A;
pub const TPM2_CreatePrimary: TPM_CC = 0x00000131;
pub const TPM2_NV_Write: TPM_CC = 0x00000137;
pub const TPM2_NV_WriteLock: TPM_CC = 0x00000138;
pub const TPM2_SelfTest: TPM_CC = 0x00000143;
pub const TPM2_Startup: TPM_CC = 0x00000144;
pub const TPM2_Shutdown: TPM_CC = 0x00000145;
pub const TPM2_NV_Read: TPM_CC = 0x0000014E;
pub const TPM2_NV_ReadLock: TPM_CC = 0x0000014F;
pub const TPM2_NV_ReadPublic: TPM_CC = 0x00000169;
pub const TPM2_ReadPublic: TPM_CC = 0x00000173;
pub const TPM2_GetCapability: TPM_CC = 0x0000017A;
pub const TPM2_GetRandom: TPM_CC = 0x0000017B;
pub const TPM2_PCR_Extend: TPM_CC = 0x00000182;
pub const TPM_HT_PCR: u32 = 0x00;
pub const TPM_HT_NV_INDEX: u32 = 0x01;
pub const HR_SHIFT: u32 = 24;
pub const HR_PCR: u32 = TPM_HT_PCR << HR_SHIFT;
pub const HR_NV_INDEX: u32 = TPM_HT_NV_INDEX << HR_SHIFT;
pub const TPM_RH_OWNER: u32 = 0x40000001;
pub const TPM_RH_NULL: u32 = 0x40000007;
pub const TPM_RH_ENDORSEMENT: u32 = 0x4000000B;
pub const TPM_RH_PLATFORM: u32 = 0x4000000C;
pub const TPM_RS_PW: u32 = 0x40000009;
/* TPM2 capabilities. */
pub const TPM_CAP_FIRST: TPM_CAP = 0x00000000;
pub const TPM_CAP_TPM_PROPERTIES: TPM_CAP = 0x00000006;
/* TPM properties */
pub const TPM_PT_NONE: TPM_PT = 0x00000000;
pub const PT_GROUP: TPM_PT = 0x00000100;
pub const PT_FIXED: TPM_PT = PT_GROUP;
pub const TPM_PT_MANUFACTURER: TPM_PT = PT_FIXED + 5;
pub const TPM_PT_VENDOR_STRING_1: TPM_PT = PT_FIXED + 6;
pub const TPM_PT_VENDOR_STRING_4: TPM_PT = PT_FIXED + 9;
pub const TPM_PT_FIRMWARE_VERSION_1: TPM_PT = PT_FIXED + 11;
pub const TPM_PT_FIRMWARE_VERSION_2: TPM_PT = PT_FIXED + 12;
pub const PT_VAR: TPM_PT = PT_GROUP * 2;
pub const TPM_PT_PERMANENT: TPM_PT = PT_VAR + 0;
pub const TPM_PT_STARTUP_CLEAR: TPM_PT = PT_VAR + 1;
/* TPM startup types. */
pub const TPM_SU_CLEAR: TPM_SU = 0x0000;
pub const TPM_SU_STATE: TPM_SU = 0x0001;
/* TPM algorithm IDs. */
pub const TPM_ALG_SHA1: TPM_ALG_ID = 0x0004;
pub const TPM_ALG_SHA256: TPM_ALG_ID = 0x000B;
pub const TPM_ALG_NULL: TPM_ALG_ID = 0x0010;
/* NV index attributes. */
pub const TPMA_NV_PPWRITE: TPMA_NV = 1 << 0;
pub const TPMA_NV_OWNERWRITE: TPMA_NV = 1 << 1;
pub const TPMA_NV_AUTHWRITE: TPMA_NV = 1 << 2;
pub const TPMA_NV_POLICYWRITE: TPMA_NV = 1 << 3;
pub const TPMA_NV_COUNTER: TPMA_NV = 1 << 4;
pub const TPMA_NV_BITS: TPMA_NV = 1 << 5;
pub const TPMA_NV_EXTEND: TPMA_NV = 1 << 6;
pub const TPMA_NV_POLICY_DELETE: TPMA_NV = 1 << 10;
pub const TPMA_NV_WRITELOCKED: TPMA_NV = 1 << 11;
pub const TPMA_NV_WRITEALL: TPMA_NV = 1 << 12;
pub const TPMA_NV_WRITEDEFINE: TPMA_NV = 1 << 13;
pub const TPMA_NV_WRITE_STCLEAR: TPMA_NV = 1 << 14;
pub const TPMA_NV_GLOBALLOCK: TPMA_NV = 1 << 15;
pub const TPMA_NV_PPREAD: TPMA_NV = 1 << 16;
pub const TPMA_NV_OWNERREAD: TPMA_NV = 1 << 17;
pub const TPMA_NV_AUTHREAD: TPMA_NV = 1 << 18;
pub const TPMA_NV_POLICYREAD: TPMA_NV = 1 << 19;
pub const TPMA_NV_NO_DA: TPMA_NV = 1 << 25;
pub const TPMA_NV_ORDERLY: TPMA_NV = 1 << 26;
pub const TPMA_NV_CLEAR_STCLEAR: TPMA_NV = 1 << 27;
pub const TPMA_NV_READLOCKED: TPMA_NV = 1 << 28;
pub const TPMA_NV_WRITTEN: TPMA_NV = 1 << 29;
pub const TPMA_NV_PLATFORMCREATE: TPMA_NV = 1 << 30;
pub const TPMA_NV_READ_STCLEAR: TPMA_NV = 1 << 31;
pub const TPMA_NV_MASK_READ: TPMA_NV =
TPMA_NV_PPREAD | TPMA_NV_OWNERREAD | TPMA_NV_AUTHREAD | TPMA_NV_POLICYREAD;
pub const TPMA_NV_MASK_WRITE: TPMA_NV =
TPMA_NV_PPWRITE | TPMA_NV_OWNERWRITE | TPMA_NV_AUTHWRITE | TPMA_NV_POLICYWRITE;
/* Starting indexes of NV index ranges, as defined in "Registry of reserved
* TPM 2.0 handles and localities".
*/
pub const TPMI_RH_NV_INDEX_TPM_START: TPMI_RH_NV_INDEX = 0x01000000;
pub const TPMI_RH_NV_INDEX_PLATFORM_START: TPMI_RH_NV_INDEX = 0x01400000;
pub const TPMI_RH_NV_INDEX_OWNER_START: TPMI_RH_NV_INDEX = 0x01800000;
pub const TPMI_RH_NV_INDEX_TCG_OEM_START: TPMI_RH_NV_INDEX = 0x01C00000;
pub const TPMI_RH_NV_INDEX_TCG_WG_START: TPMI_RH_NV_INDEX = 0x01C40000;
pub const TPMI_RH_NV_INDEX_RESERVED_START: TPMI_RH_NV_INDEX = 0x01C90000;
pub const HASH_COUNT: usize = 1; /* Only SHA-256 is supported */
/* Table 206 - Defines for SHA256 Hash Values */
pub const SHA256_DIGEST_SIZE: usize = 32;
pub type TPMI_YES_NO = u8;
pub type TPM_CC = u32;
pub type TPM_HANDLE = u32;
pub type TPMI_DH_OBJECT = TPM_HANDLE;
pub type TPMI_DH_PCR = TPM_HANDLE;
pub type TPMI_DH_PERSISTENT = TPM_HANDLE;
pub type TPMI_RH_ENABLES = TPM_HANDLE;
pub type TPMI_RH_HIERARCHY = TPM_HANDLE;
pub type TPMI_RH_NV_INDEX = TPM_HANDLE;
pub type TPMI_RH_PROVISION = TPM_HANDLE;
pub type TPM_CAP = u32;
pub type TPM_PT = u32;
pub type TPM_SU = u16;
pub type TPM_ALG_ID = u16;
pub type TPMI_ALG_HASH = TPM_ALG_ID;
pub type TPMA_NV = u32;
#[repr(C)]
pub struct TPM2B {
pub size: u16,
pub buffer: *const u8,
}
pub type TPM2B_DIGEST = TPM2B;
pub type TPM2B_AUTH = TPM2B;
pub type TPM2B_NAME = TPM2B;
#[repr(C)]
pub struct TPMS_TAGGED_PROPERTY {
pub property: TPM_PT,
pub value: u32,
}
#[repr(C)]
pub struct TPML_TAGGED_TPM_PROPERTY {
pub count: u32,
pub tpm_property: [TPMS_TAGGED_PROPERTY; 1],
}
#[repr(C)]
pub union TPMU_HA {
pub sha256: [u8; SHA256_DIGEST_SIZE],
}
#[repr(C)]
pub struct TPMT_HA {
pub hashAlg: TPMI_ALG_HASH,
pub digest: TPMU_HA,
}
#[repr(C)]
pub struct TPML_DIGEST_VALUES {
pub count: u32,
pub digests: [TPMT_HA; HASH_COUNT],
}
#[repr(C)]
pub union TPMU_CAPABILITIES {
pub tpm_properties: core::mem::ManuallyDrop<TPML_TAGGED_TPM_PROPERTY>,
}
#[repr(C)]
pub struct TPMS_CAPABILITY_DATA {
pub capability: TPM_CAP,
pub data: TPMU_CAPABILITIES,
}
#[repr(C)]
pub struct TPMS_NV_PUBLIC {
pub nvIndex: TPMI_RH_NV_INDEX,
pub nameAlg: TPMI_ALG_HASH,
pub attributes: TPMA_NV,
pub authPolicy: TPM2B,
pub dataSize: u16,
}
#[repr(C)]
pub struct tpm2_nv_define_space_cmd {
pub auth: TPM2B,
pub publicInfo: TPMS_NV_PUBLIC,
}
#[repr(C)]
pub struct tpm2_nv_undefine_space_cmd {
pub nvIndex: TPMI_RH_NV_INDEX,
pub use_platform_auth: u8,
}
#[repr(C)]
pub struct tpm2_nv_read_cmd {
pub nvIndex: TPMI_RH_NV_INDEX,
pub size: u16,
pub offset: u16,
}
#[repr(C)]
pub struct tpm2_nv_write_cmd {
pub nvIndex: TPMI_RH_NV_INDEX,
pub data: TPM2B,
pub offset: u16,
}
#[repr(C)]
pub struct tpm2_nv_read_lock_cmd {
pub nvIndex: TPMI_RH_NV_INDEX,
}
#[repr(C)]
pub struct tpm2_nv_write_lock_cmd {
pub nvIndex: TPMI_RH_NV_INDEX,
}
#[repr(C)]
pub struct tpm2_nv_read_public_cmd {
pub nvIndex: TPMI_RH_NV_INDEX,
}
#[repr(C)]
pub struct tpm2_hierarchy_control_cmd {
pub enable: TPMI_RH_ENABLES,
pub state: TPMI_YES_NO,
}
#[repr(C)]
pub struct tpm2_get_capability_cmd {
pub capability: TPM_CAP,
pub property: u32,
pub property_count: u32,
}
#[repr(C)]
pub struct tpm2_get_random_cmd {
pub bytes_requested: u16,
}
#[repr(C)]
pub struct tpm2_self_test_cmd {
pub full_test: TPMI_YES_NO,
}
#[repr(C)]
pub struct tpm2_startup_cmd {
pub startup_type: TPM_SU,
}
#[repr(C)]
pub struct tpm2_shutdown_cmd {
pub shutdown_type: TPM_SU,
}
#[repr(C)]
pub struct tpm2_pcr_extend_cmd {
pub pcrHandle: TPMI_DH_PCR,
pub digests: TPML_DIGEST_VALUES,
}
#[repr(C)]
pub struct tpm2_read_public_cmd {
pub object_handle: TPMI_DH_OBJECT,
}
#[repr(C)]
pub struct tpm2_evict_control_cmd {
pub auth: TPMI_RH_PROVISION,
pub object_handle: TPMI_DH_OBJECT,
pub persistent_handle: TPMI_DH_PERSISTENT,
}
#[repr(C)]
pub struct tpm2_create_primary_cmd {
pub primary_handle: TPMI_RH_HIERARCHY,
pub in_sensitive: TPM2B,
pub in_public: TPM2B,
}
/* Common command/response header. */
#[repr(C, packed)]
pub struct tpm_header {
pub tpm_tag: u16,
pub tpm_size: u32,
pub tpm_code: u32,
}
#[repr(C)]
pub struct nv_read_response {
pub params_size: u32,
pub buffer: TPM2B,
}
#[repr(C)]
pub struct read_public_response {
pub buffer: TPM2B,
}
/* tpm2_session_attrs represents an 8-bit packed bitfield:
* bit 0: continueSession
* bit 1: auditExclusive
* bit 2: auditReset
* bits 3-4: reserved3_4
* bit 5: decrypt
* bit 6: encrypt
* bit 7: audit
*/
#[repr(transparent)]
#[derive(Copy, Clone)]
pub struct tpm2_session_attrs(pub u8);
impl tpm2_session_attrs {
pub const CONTINUE_SESSION: u8 = 1 << 0;
pub const AUDIT_EXCLUSIVE: u8 = 1 << 1;
pub const AUDIT_RESET: u8 = 1 << 2;
/* bits 3-4: reserved3_4 */
pub const DECRYPT: u8 = 1 << 5;
pub const ENCRYPT: u8 = 1 << 6;
pub const AUDIT: u8 = 1 << 7;
}
#[repr(C)]
pub union tpm2_session_attrs_union {
pub session_attr_bits: tpm2_session_attrs,
pub session_attrs: u8,
}
#[repr(C)]
pub struct tpm2_session_header {
pub session_handle: u32,
pub nonce_size: u16,
pub nonce: *mut u8,
pub attrs: tpm2_session_attrs_union,
pub auth_size: u16,
pub auth: *mut u8,
}
#[repr(C, packed)]
pub struct get_capability_response {
pub more_data: TPMI_YES_NO,
pub capability_data: TPMS_CAPABILITY_DATA,
}
#[repr(C, packed)]
pub struct get_random_response {
pub random_bytes: TPM2B_DIGEST,
}
#[repr(C, packed)]
pub struct nv_read_public_response {
pub nvPublic: TPMS_NV_PUBLIC,
pub nvName: TPM2B_NAME,
}
#[repr(C, packed)]
pub struct create_primary_response {
pub object_handle: TPM_HANDLE,
}
#[repr(C)]
pub union tpm2_response_body {
pub nvr: core::mem::ManuallyDrop<nv_read_response>,
pub def_space: core::mem::ManuallyDrop<tpm2_session_header>,
pub cap: core::mem::ManuallyDrop<get_capability_response>,
pub random: core::mem::ManuallyDrop<get_random_response>,
pub nv_read_public: core::mem::ManuallyDrop<nv_read_public_response>,
pub read_pub: core::mem::ManuallyDrop<read_public_response>,
pub create_primary: core::mem::ManuallyDrop<create_primary_response>,
}
#[repr(C)]
pub struct tpm2_response {
pub hdr: tpm_header,
pub body: tpm2_response_body,
}
/* TPM_PERMANENT_FLAGS represents a 32-bit packed bitfield:
* bit 0: ownerAuthSet
* bit 1: endorsementAuthSet
* bit 2: lockoutAuthSet
* bits 3-7: reserved3_7
* bit 8: disableClear
* bit 9: inLockout
* bit 10: tpmGeneratedEPS
* bits 11-31: reserved11_31
*/
#[repr(transparent)]
pub struct TPM_PERMANENT_FLAGS(pub u32);
impl TPM_PERMANENT_FLAGS {
pub const OWNER_AUTH_SET: u32 = 1 << 0;
pub const ENDORSEMENT_AUTH_SET: u32 = 1 << 1;
pub const LOCKOUT_AUTH_SET: u32 = 1 << 2;
/* bits 3-7: reserved3_7 */
pub const DISABLE_CLEAR: u32 = 1 << 8;
pub const IN_LOCKOUT: u32 = 1 << 9;
pub const TPM_GENERATED_EPS: u32 = 1 << 10;
/* bits 11-31: reserved11_31 */
}
/* TPM_STCLEAR_FLAGS represents a 32-bit packed bitfield:
* bit 0: phEnable
* bit 1: shEnable
* bit 2: ehEnable
* bit 3: phEnableNV
* bits 4-30: reserved4_30
* bit 31: orderly
*/
#[repr(transparent)]
pub struct TPM_STCLEAR_FLAGS(pub u32);
impl TPM_STCLEAR_FLAGS {
pub const PH_ENABLE: u32 = 1 << 0;
pub const SH_ENABLE: u32 = 1 << 1;
pub const EH_ENABLE: u32 = 1 << 2;
pub const PH_ENABLE_NV: u32 = 1 << 3;
/* bits 4-30: reserved4_30 */
pub const ORDERLY: u32 = 1 << 31;
}
#[repr(C)]
pub struct TPM_IFX_FIELDUPGRADEINFO;
/* TODO(apronin): For TPM2 certain properties must be received using
* TPM2_GetCapability instead of being hardcoded as they are now:
* TPM_MAX_COMMAND_SIZE -> use TPM_PT_MAX_COMMAND_SIZE for TPM2.
* TPM_PCR_DIGEST -> use TPM_PT_MAX_DIGEST for TPM2.
*/
pub const TPM_MAX_COMMAND_SIZE: usize = 4096;
pub const TPM_PCR_DIGEST: usize = 32;
----- END ./src/tlcl/tpm20/constants.rs -----
----- ./src/tlcl/tpm20/types.rs -----
#![allow(non_camel_case_types)]
// Table 17 - Definition of TPM_RC Constants <OUT>
pub type TpmRc = u32;
pub const TPM_RC_SUCCESS: TpmRc = 0x000;
pub const TPM_RC_BAD_TAG: TpmRc = 0x01E;
const RC_VER1: TpmRc = 0x100;
pub const TPM_RC_INITIALIZE: TpmRc = RC_VER1 + 0x000;
pub const TPM_RC_FAILURE: TpmRc = RC_VER1 + 0x001;
pub const TPM_RC_SEQUENCE: TpmRc = RC_VER1 + 0x003;
pub const TPM_RC_PRIVATE: TpmRc = RC_VER1 + 0x00B;
pub const TPM_RC_HMAC: TpmRc = RC_VER1 + 0x019;
pub const TPM_RC_DISABLED: TpmRc = RC_VER1 + 0x020;
pub const TPM_RC_EXCLUSIVE: TpmRc = RC_VER1 + 0x021;
pub const TPM_RC_AUTH_TYPE: TpmRc = RC_VER1 + 0x024;
pub const TPM_RC_AUTH_MISSING: TpmRc = RC_VER1 + 0x025;
pub const TPM_RC_POLICY: TpmRc = RC_VER1 + 0x026;
pub const TPM_RC_PCR: TpmRc = RC_VER1 + 0x027;
pub const TPM_RC_PCR_CHANGED: TpmRc = RC_VER1 + 0x028;
pub const TPM_RC_UPGRADE: TpmRc = RC_VER1 + 0x02D;
pub const TPM_RC_TOO_MANY_CONTEXTS: TpmRc = RC_VER1 + 0x02E;
pub const TPM_RC_AUTH_UNAVAILABLE: TpmRc = RC_VER1 + 0x02F;
pub const TPM_RC_REBOOT: TpmRc = RC_VER1 + 0x030;
pub const TPM_RC_UNBALANCED: TpmRc = RC_VER1 + 0x031;
pub const TPM_RC_COMMAND_SIZE: TpmRc = RC_VER1 + 0x042;
pub const TPM_RC_COMMAND_CODE: TpmRc = RC_VER1 + 0x043;
pub const TPM_RC_AUTHSIZE: TpmRc = RC_VER1 + 0x044;
pub const TPM_RC_AUTH_CONTEXT: TpmRc = RC_VER1 + 0x045;
pub const TPM_RC_NV_RANGE: TpmRc = RC_VER1 + 0x046;
pub const TPM_RC_NV_SIZE: TpmRc = RC_VER1 + 0x047;
pub const TPM_RC_NV_LOCKED: TpmRc = RC_VER1 + 0x048;
pub const TPM_RC_NV_AUTHORIZATION: TpmRc = RC_VER1 + 0x049;
pub const TPM_RC_NV_UNINITIALIZED: TpmRc = RC_VER1 + 0x04A;
pub const TPM_RC_NV_SPACE: TpmRc = RC_VER1 + 0x04B;
pub const TPM_RC_NV_DEFINED: TpmRc = RC_VER1 + 0x04C;
pub const TPM_RC_BAD_CONTEXT: TpmRc = RC_VER1 + 0x050;
pub const TPM_RC_CPHASH: TpmRc = RC_VER1 + 0x051;
pub const TPM_RC_PARENT: TpmRc = RC_VER1 + 0x052;
pub const TPM_RC_NEEDS_TEST: TpmRc = RC_VER1 + 0x053;
pub const TPM_RC_NO_RESULT: TpmRc = RC_VER1 + 0x054;
pub const TPM_RC_SENSITIVE: TpmRc = RC_VER1 + 0x055;
pub const RC_MAX_FM0: TpmRc = RC_VER1 + 0x07F;
const RC_FMT1: TpmRc = 0x080;
pub const TPM_RC_ASYMMETRIC: TpmRc = RC_FMT1 + 0x001;
pub const TPM_RC_ATTRIBUTES: TpmRc = RC_FMT1 + 0x002;
pub const TPM_RC_HASH: TpmRc = RC_FMT1 + 0x003;
pub const TPM_RC_VALUE: TpmRc = RC_FMT1 + 0x004;
pub const TPM_RC_HIERARCHY: TpmRc = RC_FMT1 + 0x005;
pub const TPM_RC_KEY_SIZE: TpmRc = RC_FMT1 + 0x007;
pub const TPM_RC_MGF: TpmRc = RC_FMT1 + 0x008;
pub const TPM_RC_MODE: TpmRc = RC_FMT1 + 0x009;
pub const TPM_RC_TYPE: TpmRc = RC_FMT1 + 0x00A;
pub const TPM_RC_HANDLE: TpmRc = RC_FMT1 + 0x00B;
pub const TPM_RC_KDF: TpmRc = RC_FMT1 + 0x00C;
pub const TPM_RC_RANGE: TpmRc = RC_FMT1 + 0x00D;
pub const TPM_RC_AUTH_FAIL: TpmRc = RC_FMT1 + 0x00E;
pub const TPM_RC_NONCE: TpmRc = RC_FMT1 + 0x00F;
pub const TPM_RC_PP: TpmRc = RC_FMT1 + 0x010;
pub const TPM_RC_SCHEME: TpmRc = RC_FMT1 + 0x012;
pub const TPM_RC_SIZE: TpmRc = RC_FMT1 + 0x015;
pub const TPM_RC_SYMMETRIC: TpmRc = RC_FMT1 + 0x016;
pub const TPM_RC_TAG: TpmRc = RC_FMT1 + 0x017;
pub const TPM_RC_SELECTOR: TpmRc = RC_FMT1 + 0x018;
pub const TPM_RC_INSUFFICIENT: TpmRc = RC_FMT1 + 0x01A;
pub const TPM_RC_SIGNATURE: TpmRc = RC_FMT1 + 0x01B;
pub const TPM_RC_KEY: TpmRc = RC_FMT1 + 0x01C;
pub const TPM_RC_POLICY_FAIL: TpmRc = RC_FMT1 + 0x01D;
pub const TPM_RC_INTEGRITY: TpmRc = RC_FMT1 + 0x01F;
pub const TPM_RC_TICKET: TpmRc = RC_FMT1 + 0x020;
pub const TPM_RC_RESERVED_BITS: TpmRc = RC_FMT1 + 0x021;
pub const TPM_RC_BAD_AUTH: TpmRc = RC_FMT1 + 0x022;
pub const TPM_RC_EXPIRED: TpmRc = RC_FMT1 + 0x023;
pub const TPM_RC_POLICY_CC: TpmRc = RC_FMT1 + 0x024;
pub const TPM_RC_BINDING: TpmRc = RC_FMT1 + 0x025;
pub const TPM_RC_CURVE: TpmRc = RC_FMT1 + 0x026;
pub const TPM_RC_ECC_POINT: TpmRc = RC_FMT1 + 0x027;
const RC_WARN: TpmRc = 0x900;
pub const TPM_RC_CONTEXT_GAP: TpmRc = RC_WARN + 0x001;
pub const TPM_RC_OBJECT_MEMORY: TpmRc = RC_WARN + 0x002;
pub const TPM_RC_SESSION_MEMORY: TpmRc = RC_WARN + 0x003;
pub const TPM_RC_MEMORY: TpmRc = RC_WARN + 0x004;
pub const TPM_RC_SESSION_HANDLES: TpmRc = RC_WARN + 0x005;
pub const TPM_RC_OBJECT_HANDLES: TpmRc = RC_WARN + 0x006;
pub const TPM_RC_LOCALITY: TpmRc = RC_WARN + 0x007;
pub const TPM_RC_YIELDED: TpmRc = RC_WARN + 0x008;
pub const TPM_RC_CANCELED: TpmRc = RC_WARN + 0x009;
pub const TPM_RC_TESTING: TpmRc = RC_WARN + 0x00A;
pub const TPM_RC_REFERENCE_H0: TpmRc = RC_WARN + 0x010;
pub const TPM_RC_REFERENCE_H1: TpmRc = RC_WARN + 0x011;
pub const TPM_RC_REFERENCE_H2: TpmRc = RC_WARN + 0x012;
pub const TPM_RC_REFERENCE_H3: TpmRc = RC_WARN + 0x013;
pub const TPM_RC_REFERENCE_H4: TpmRc = RC_WARN + 0x014;
pub const TPM_RC_REFERENCE_H5: TpmRc = RC_WARN + 0x015;
pub const TPM_RC_REFERENCE_H6: TpmRc = RC_WARN + 0x016;
pub const TPM_RC_REFERENCE_S0: TpmRc = RC_WARN + 0x018;
pub const TPM_RC_REFERENCE_S1: TpmRc = RC_WARN + 0x019;
pub const TPM_RC_REFERENCE_S2: TpmRc = RC_WARN + 0x01A;
pub const TPM_RC_REFERENCE_S3: TpmRc = RC_WARN + 0x01B;
pub const TPM_RC_REFERENCE_S4: TpmRc = RC_WARN + 0x01C;
pub const TPM_RC_REFERENCE_S5: TpmRc = RC_WARN + 0x01D;
pub const TPM_RC_REFERENCE_S6: TpmRc = RC_WARN + 0x01E;
pub const TPM_RC_NV_RATE: TpmRc = RC_WARN + 0x020;
pub const TPM_RC_LOCKOUT: TpmRc = RC_WARN + 0x021;
pub const TPM_RC_RETRY: TpmRc = RC_WARN + 0x022;
pub const TPM_RC_NV_UNAVAILABLE: TpmRc = RC_WARN + 0x023;
pub const TPM_RC_NOT_USED: TpmRc = RC_WARN + 0x07F;
// Parameter/handle qualifiers
pub const TPM_RC_H: TpmRc = 0x000;
pub const TPM_RC_P: TpmRc = 0x040;
pub const TPM_RC_S: TpmRc = 0x800;
pub const TPM_RC_1: TpmRc = 0x100;
pub const TPM_RC_2: TpmRc = 0x200;
pub const TPM_RC_3: TpmRc = 0x300;
pub const TPM_RC_4: TpmRc = 0x400;
pub const TPM_RC_5: TpmRc = 0x500;
pub const TPM_RC_6: TpmRc = 0x600;
pub const TPM_RC_7: TpmRc = 0x700;
pub const TPM_RC_8: TpmRc = 0x800;
pub const TPM_RC_9: TpmRc = 0x900;
pub const TPM_RC_A: TpmRc = 0xA00;
pub const TPM_RC_B: TpmRc = 0xB00;
pub const TPM_RC_C: TpmRc = 0xC00;
pub const TPM_RC_D: TpmRc = 0xD00;
pub const TPM_RC_E: TpmRc = 0xE00;
pub const TPM_RC_F: TpmRc = 0xF00;
pub const TPM_RC_N_MASK: TpmRc = 0xF00;
----- END ./src/tlcl/tpm20/types.rs -----
----- ./src/tlcl/tpm20/mod.rs -----
#![allow(unused_assignments)]
#![allow(unused_mut)]
pub mod constants;
pub mod marshal;
pub mod types;
pub mod unmarshal;
pub fn tpm_get_response(
command: constants::TPM_CC,
command_body: *const core::ffi::c_void,
response: &mut constants::tpm2_response,
) -> u32 {
crate::tlcl::backend::tpm_get_response::<{ constants::TPM_BUFFER_SIZE }, _, _, _, _>(
command,
command_body,
response,
marshal::tpm_marshal_command,
unmarshal::tpm_unmarshal_response,
|resp| unsafe { core::ptr::read_unaligned(core::ptr::addr_of!(resp.hdr.tpm_code)) },
)
}
pub fn tpm_send_receive(
command: constants::TPM_CC,
command_body: *const core::ffi::c_void,
response: &mut constants::tpm2_response,
) -> u32 {
let rv = tpm_get_response(command, command_body, response);
if rv != crate::tlcl::constants::TPM_SUCCESS {
return rv;
}
unsafe { core::ptr::read_unaligned(core::ptr::addr_of!(response.hdr.tpm_code)) }
}
pub fn tpm_get_response_code(
command: constants::TPM_CC,
command_body: *const core::ffi::c_void,
) -> u32 {
let mut response: constants::tpm2_response = unsafe { core::mem::zeroed() };
tpm_send_receive(command, command_body, &mut response)
}
----- END ./src/tlcl/tpm20/mod.rs -----
----- ./src/tlcl/tpm20/unmarshal.rs -----
#![allow(non_upper_case_globals)]
#![allow(non_snake_case)]
use crate::{
LOG_DBG,
tlcl::{
bytes::{read_be16, read_be32},
tpm20::constants::{
TPM_CC, TPM_ST_NO_SESSIONS, TPM_ST_SESSIONS, TPM2_Clear, TPM2_NV_DefineSpace, TPM2_NV_Read,
TPM2_NV_ReadPublic, TPM2_NV_UndefineSpace, TPM2_NV_Write, TPM2_Shutdown, TPM2_Startup, TPM2B,
TPMS_NV_PUBLIC, nv_read_public_response, nv_read_response, tpm2_response,
},
},
};
pub fn unmarshal_u8(buffer: &mut *const u8, buffer_space: &mut i32) -> u8 {
let value: u8;
if *buffer_space < core::mem::size_of::<u8>() as i32 {
*buffer_space = -1;
return 0;
}
unsafe {
value = **buffer;
*buffer = (*buffer).add(core::mem::size_of::<u8>());
}
*buffer_space -= core::mem::size_of::<u8>() as i32;
value
}
pub fn unmarshal_u16(buffer: &mut *const u8, buffer_space: &mut i32) -> u16 {
let value: u16;
if *buffer_space < core::mem::size_of::<u16>() as i32 {
*buffer_space = -1;
return 0;
}
value = read_be16(*buffer);
unsafe {
*buffer = (*buffer).add(core::mem::size_of::<u16>());
}
*buffer_space -= core::mem::size_of::<u16>() as i32;
value
}
pub fn unmarshal_u32(buffer: &mut *const u8, buffer_space: &mut i32) -> u32 {
let value: u32;
if *buffer_space < core::mem::size_of::<u32>() as i32 {
*buffer_space = -1;
return 0;
}
value = read_be32(*buffer);
unsafe {
*buffer = (*buffer).add(core::mem::size_of::<u32>());
}
*buffer_space -= core::mem::size_of::<u32>() as i32;
value
}
pub fn unmarshal_TPM2B(buffer: &mut *const u8, size: &mut i32, tpm2b: &mut TPM2B) {
tpm2b.size = unmarshal_u16(buffer, size);
if tpm2b.size as i32 > *size {
tpm2b.buffer = core::ptr::null();
tpm2b.size = 0;
*size = -1;
return;
}
tpm2b.buffer = *buffer;
unsafe {
*buffer = (*buffer).add(tpm2b.size as usize);
}
*size -= tpm2b.size as i32;
}
pub fn unmarshal_authorization_section(buffer: &mut *const u8, size: &mut i32, cmd_name: &str) {
if *size != 5 {
LOG_DBG!(
"unexepcted authorization section size {} for {}",
*size,
&cmd_name
);
}
*buffer = ((*buffer as usize) + (*size as usize)) as *const u8;
*size = 0;
}
pub fn unmarshal_nv_read(buffer: &mut *const u8, size: &mut i32, nvr: &mut nv_read_response) {
nvr.params_size = unmarshal_u32(buffer, size);
unmarshal_TPM2B(buffer, size, &mut nvr.buffer);
if nvr.params_size != (nvr.buffer.size as u32 + core::mem::size_of::<u16>() as u32) {
return;
}
if *size < 0 {
return;
}
unmarshal_authorization_section(buffer, size, "NV_Read");
}
pub fn unmarshal_nv_write(buffer: &mut *const u8, size: &mut i32) {
unmarshal_authorization_section(buffer, size, "NV_Write");
}
pub fn unmarshal_TPMS_NV_PUBLIC(
buffer: &mut *const u8,
size: &mut i32,
pub_data: &mut TPMS_NV_PUBLIC,
) {
let mut tpm2b_size = unmarshal_u16(buffer, size) as i32;
if tpm2b_size > *size {
*size = -1;
return;
}
*size -= tpm2b_size;
pub_data.nvIndex = unmarshal_u32(buffer, &mut tpm2b_size);
pub_data.nameAlg = unmarshal_u16(buffer, &mut tpm2b_size);
pub_data.attributes = unmarshal_u32(buffer, &mut tpm2b_size);
unmarshal_TPM2B(buffer, &mut tpm2b_size, &mut pub_data.authPolicy);
pub_data.dataSize = unmarshal_u16(buffer, &mut tpm2b_size);
if tpm2b_size != 0 {
*size = -1;
}
}
pub fn unmarshal_nv_read_public(
buffer: &mut *const u8,
size: &mut i32,
nv_pub: &mut nv_read_public_response,
) {
let mut nv_public: TPMS_NV_PUBLIC = unsafe { core::mem::zeroed() };
let mut nv_name: TPM2B = unsafe { core::mem::zeroed() };
unmarshal_TPMS_NV_PUBLIC(buffer, size, &mut nv_public);
unmarshal_TPM2B(buffer, size, &mut nv_name);
if *size < 0 {
return;
}
if *size > 0 {
*size = -1;
return;
}
unsafe {
core::ptr::addr_of_mut!(nv_pub.nvPublic).write_unaligned(nv_public);
core::ptr::addr_of_mut!(nv_pub.nvName).write_unaligned(nv_name);
}
}
pub fn tpm_unmarshal_response(
command: TPM_CC,
response_body: *const core::ffi::c_void,
mut cr_size: i32,
response: &mut tpm2_response,
) -> i32 {
let mut buffer = response_body as *const u8;
if cr_size < core::mem::size_of::<crate::tlcl::tpm20::constants::tpm_header>() as i32 {
return -1;
}
response.hdr.tpm_tag = unmarshal_u16(&mut buffer, &mut cr_size);
response.hdr.tpm_size = unmarshal_u32(&mut buffer, &mut cr_size);
response.hdr.tpm_code = unmarshal_u32(&mut buffer, &mut cr_size);
if response.hdr.tpm_tag != TPM_ST_NO_SESSIONS && response.hdr.tpm_tag != TPM_ST_SESSIONS {
return -1;
}
if response.hdr.tpm_size
< core::mem::size_of::<crate::tlcl::tpm20::constants::tpm_header>() as u32
{
return -1;
}
if cr_size == 0 {
return 0;
}
match command {
TPM2_NV_Read => {
let mut nvr = nv_read_response {
params_size: 0,
buffer: TPM2B {
size: 0,
buffer: core::ptr::null(),
},
};
unmarshal_nv_read(&mut buffer, &mut cr_size, &mut nvr);
response.body.nvr = core::mem::ManuallyDrop::new(nvr);
}
TPM2_NV_ReadPublic => {
let mut nv_pub: nv_read_public_response = unsafe { core::mem::zeroed() };
unmarshal_nv_read_public(&mut buffer, &mut cr_size, &mut nv_pub);
response.body.nv_read_public = core::mem::ManuallyDrop::new(nv_pub);
}
TPM2_NV_Write
| TPM2_NV_DefineSpace
| TPM2_NV_UndefineSpace
| TPM2_Clear
| TPM2_Startup
| TPM2_Shutdown => {
cr_size = 0;
}
_ => {
return -1;
}
}
if cr_size != 0 {
return -1;
}
0
}
----- END ./src/tlcl/tpm20/unmarshal.rs -----
----- ./src/tlcl/tpm20/marshal.rs -----
#![allow(non_upper_case_globals)]
#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
use crate::{
key_types, keys,
keyval::KvValue,
kv_get, kv_set,
tlcl::{
bytes::{write_be16, write_be32},
tpm20::constants::{
TPM_RH_OWNER, TPM_RH_PLATFORM, TPM_RS_PW, TPM_ST_NO_SESSIONS, TPM_ST_SESSIONS, TPM2_Clear,
TPM2_NV_DefineSpace, TPM2_NV_Read, TPM2_NV_ReadPublic, TPM2_NV_UndefineSpace, TPM2_NV_Write,
TPM2_Shutdown, TPM2_Startup, TPM2B, TPMA_NV_PLATFORMCREATE, TPMI_RH_NV_INDEX_OWNER_START,
TPMS_NV_PUBLIC, tpm_header, tpm2_nv_define_space_cmd, tpm2_nv_read_cmd,
tpm2_nv_read_public_cmd, tpm2_nv_undefine_space_cmd, tpm2_nv_write_cmd, tpm2_session_header,
tpm2_shutdown_cmd, tpm2_startup_cmd,
},
},
};
pub fn marshal_blob(
buffer: &mut *mut u8,
blob: *const u8,
blob_size: usize,
buffer_space: &mut i32,
) {
if *buffer_space < blob_size as i32 {
*buffer_space = -1;
return;
}
unsafe {
core::ptr::copy_nonoverlapping(blob, *buffer, blob_size);
*buffer = ((*buffer as usize) + blob_size) as *mut u8;
}
*buffer_space -= blob_size as i32;
}
pub fn marshal_u8(buffer: &mut *mut u8, value: u8, buffer_space: &mut i32) {
let mut bp = *buffer;
if *buffer_space < core::mem::size_of::<u8>() as i32 {
*buffer_space = -1;
return;
}
unsafe {
*bp = value;
bp = bp.add(1);
}
*buffer = bp;
*buffer_space -= core::mem::size_of::<u8>() as i32;
}
pub fn marshal_u16(buffer: &mut *mut u8, value: u16, buffer_space: &mut i32) {
if *buffer_space < core::mem::size_of::<u16>() as i32 {
*buffer_space = -1;
return;
}
write_be16(*buffer, value);
unsafe {
*buffer = (*buffer).add(core::mem::size_of::<u16>());
}
*buffer_space -= core::mem::size_of::<u16>() as i32;
}
pub fn marshal_u32(buffer: &mut *mut u8, value: u32, buffer_space: &mut i32) {
if *buffer_space < core::mem::size_of::<u32>() as i32 {
*buffer_space = -1;
return;
}
write_be32(*buffer, value);
unsafe {
*buffer = (*buffer).add(core::mem::size_of::<u32>());
}
*buffer_space -= core::mem::size_of::<u32>() as i32;
}
pub fn marshal_TPM2B(buffer: &mut *mut u8, data: &TPM2B, buffer_space: &mut i32) {
let total_size: usize = data.size as usize + std::mem::size_of::<u16>();
if total_size > *buffer_space as usize {
*buffer_space = -1;
return;
}
marshal_u16(buffer, data.size, buffer_space);
if data.size == 0 {
return;
}
unsafe {
core::ptr::copy_nonoverlapping(data.buffer, *buffer, data.size as usize);
*buffer = (*buffer).add(data.size as usize);
}
*buffer_space -= data.size as i32;
}
pub fn marshal_TPMS_NV_PUBLIC(
mut buffer: &mut *mut u8,
data: &mut TPMS_NV_PUBLIC,
buffer_space: &mut i32,
) {
let mut size_field: tpm2_marshal_size_field = unsafe { std::mem::zeroed() };
/* Skip room for the size. */
marshal_reserve_size_field(
buffer,
&mut size_field,
std::mem::size_of::<u16>() as i32,
buffer_space,
);
marshal_TPM_HANDLE(&mut buffer, data.nvIndex, buffer_space);
marshal_ALG_ID(&mut buffer, data.nameAlg, buffer_space);
marshal_u32(&mut buffer, data.attributes, buffer_space);
marshal_TPM2B(&mut buffer, &data.authPolicy, buffer_space);
marshal_u16(&mut buffer, data.dataSize, buffer_space);
marshal_fill_size_field(buffer, &mut size_field, false, buffer_space);
}
#[inline(always)]
pub fn marshal_TPM_HANDLE(buffer: &mut *mut u8, value: u32, buffer_space: &mut i32) {
marshal_u32(buffer, value, buffer_space);
}
#[inline(always)]
pub fn marshal_TPM_SU(buffer: &mut *mut u8, value: u16, buffer_space: &mut i32) {
marshal_u16(buffer, value, buffer_space);
}
#[inline(always)]
pub fn marshal_ALG_ID(buffer: &mut *mut u8, value: u16, buffer_space: &mut i32) {
marshal_u16(buffer, value, buffer_space);
}
#[inline(always)]
pub fn marshal_TPMI_ALG_HASH(buffer: &mut *mut u8, value: u16, buffer_space: &mut i32) {
marshal_u16(buffer, value, buffer_space);
}
pub struct tpm2_marshal_size_field {
pub size: i32,
pub location: *mut u8,
}
pub fn marshal_reserve_size_field(
buffer: &mut *mut u8,
field: &mut tpm2_marshal_size_field,
field_size: i32,
buffer_space: &mut i32,
) {
if field_size != core::mem::size_of::<u16>() as i32
&& field_size != core::mem::size_of::<u32>() as i32
{
*buffer_space = -1;
return;
}
if *buffer_space < field_size {
*buffer_space = -1;
return;
}
field.size = field_size;
field.location = *buffer;
*buffer_space -= field_size;
*buffer = ((*buffer as usize) + field_size as usize) as *mut u8;
}
pub fn marshal_fill_size_field(
buffer: &mut *mut u8,
field: &mut tpm2_marshal_size_field,
include_size_field: bool,
buffer_space: &mut i32,
) {
let mut size = (*buffer as usize) - (field.location as usize);
if *buffer_space < 0 {
return;
}
if !include_size_field {
size -= field.size as usize;
}
if field.size == core::mem::size_of::<u32>() as i32 {
marshal_u32(&mut field.location, size as u32, &mut field.size);
} else {
marshal_u16(&mut field.location, size as u16, &mut field.size);
}
}
pub fn marshal_session_header(
buffer: &mut *mut u8,
session_header: &tpm2_session_header,
buffer_space: &mut i32,
) {
let mut size_field = tpm2_marshal_size_field {
size: 0,
location: core::ptr::null_mut(),
};
marshal_reserve_size_field(
buffer,
&mut size_field,
core::mem::size_of::<u32>() as i32,
buffer_space,
);
marshal_u32(buffer, session_header.session_handle, buffer_space);
marshal_u16(buffer, session_header.nonce_size, buffer_space);
marshal_blob(
buffer,
session_header.nonce as *const u8,
session_header.nonce_size as usize,
buffer_space,
);
let session_attrs = unsafe { session_header.attrs.session_attrs };
marshal_u8(buffer, session_attrs, buffer_space);
marshal_u16(buffer, session_header.auth_size, buffer_space);
marshal_blob(
buffer,
session_header.auth as *const u8,
session_header.auth_size as usize,
buffer_space,
);
marshal_fill_size_field(buffer, &mut size_field, false, buffer_space);
}
fn get_nv_index_write_auth(nv_index: u32) -> u32 {
if nv_index >= TPMI_RH_NV_INDEX_OWNER_START {
nv_index
} else {
TPM_RH_PLATFORM
}
}
pub fn marshal_clear(
mut buffer: *mut u8,
_command_body: *const core::ffi::c_void,
buffer_space: &mut i32,
) {
let mut session_header: tpm2_session_header;
kv_set(keys::TPM_TAG, TPM_ST_SESSIONS);
marshal_TPM_HANDLE(&mut buffer, TPM_RH_PLATFORM, buffer_space);
unsafe {
session_header = std::mem::zeroed();
}
session_header.session_handle = TPM_RS_PW;
marshal_session_header(&mut buffer, &session_header, buffer_space);
}
pub fn marshal_nv_read(
mut buffer: *mut u8,
command_body: *mut tpm2_nv_read_cmd,
buffer_space: &mut i32,
) {
let mut session_header: tpm2_session_header;
let command_body_ref: &tpm2_nv_read_cmd;
if command_body.is_null() {
*buffer_space = -1;
return;
}
unsafe {
command_body_ref = &*command_body;
}
if let Some(KvValue::Bool(true)) = kv_get(key_types::BOOL, keys::PH_DISABLED) {
marshal_TPM_HANDLE(&mut buffer, command_body_ref.nvIndex, buffer_space)
} else {
marshal_TPM_HANDLE(&mut buffer, TPM_RH_PLATFORM, buffer_space)
}
marshal_TPM_HANDLE(&mut buffer, command_body_ref.nvIndex, buffer_space);
unsafe {
session_header = std::mem::zeroed();
}
session_header.session_handle = TPM_RS_PW;
marshal_session_header(&mut buffer, &session_header, buffer_space);
kv_set(keys::TPM_TAG, TPM_ST_SESSIONS);
marshal_u16(&mut buffer, command_body_ref.size, buffer_space);
marshal_u16(&mut buffer, command_body_ref.offset, buffer_space);
}
pub fn marshal_nv_write(
mut buffer: *mut u8,
command_body: *mut tpm2_nv_write_cmd,
buffer_space: &mut i32,
) {
let mut session_header: tpm2_session_header;
let command_body_ref: &tpm2_nv_write_cmd;
if command_body.is_null() {
*buffer_space = -1;
return;
}
unsafe {
command_body_ref = &*command_body;
}
marshal_TPM_HANDLE(
&mut buffer,
get_nv_index_write_auth(command_body_ref.nvIndex),
buffer_space,
);
marshal_TPM_HANDLE(&mut buffer, command_body_ref.nvIndex, buffer_space);
unsafe {
session_header = std::mem::zeroed();
}
session_header.session_handle = TPM_RS_PW;
marshal_session_header(&mut buffer, &session_header, buffer_space);
kv_set(keys::TPM_TAG, TPM_ST_SESSIONS);
marshal_TPM2B(&mut buffer, &command_body_ref.data, buffer_space);
marshal_u16(&mut buffer, command_body_ref.offset, buffer_space);
}
pub fn marshal_nv_define_space(
mut buffer: *mut u8,
command_body: *mut tpm2_nv_define_space_cmd,
buffer_space: &mut i32,
) {
let mut session_header: tpm2_session_header;
let command_body_ref: &mut tpm2_nv_define_space_cmd;
unsafe {
command_body_ref = &mut *command_body;
}
if command_body_ref.publicInfo.attributes & TPMA_NV_PLATFORMCREATE != 0 {
marshal_TPM_HANDLE(&mut buffer, TPM_RH_PLATFORM, buffer_space)
} else {
marshal_TPM_HANDLE(&mut buffer, TPM_RH_OWNER, buffer_space)
}
unsafe {
session_header = std::mem::zeroed();
}
session_header.session_handle = TPM_RS_PW;
marshal_session_header(&mut buffer, &session_header, buffer_space);
kv_set(keys::TPM_TAG, TPM_ST_SESSIONS);
marshal_TPM2B(&mut buffer, &command_body_ref.auth, buffer_space);
marshal_TPMS_NV_PUBLIC(&mut buffer, &mut command_body_ref.publicInfo, buffer_space);
}
pub fn marshal_nv_read_public(
mut buffer: *mut u8,
command_body: *mut tpm2_nv_read_public_cmd,
buffer_space: &mut i32,
) {
if command_body.is_null() {
*buffer_space = -1;
return;
}
let command_body_ref = unsafe { &*command_body };
marshal_TPM_HANDLE(&mut buffer, command_body_ref.nvIndex, buffer_space);
}
pub fn marshal_nv_undefine_space(
mut buffer: *mut u8,
command_body: *mut tpm2_nv_undefine_space_cmd,
buffer_space: &mut i32,
) {
let mut session_header: tpm2_session_header;
let command_body_ref: &tpm2_nv_undefine_space_cmd;
if command_body.is_null() {
*buffer_space = -1;
return;
}
unsafe {
command_body_ref = &*command_body;
}
if command_body_ref.use_platform_auth != 0 {
marshal_TPM_HANDLE(&mut buffer, TPM_RH_PLATFORM, buffer_space);
} else {
marshal_TPM_HANDLE(&mut buffer, TPM_RH_OWNER, buffer_space);
}
marshal_TPM_HANDLE(&mut buffer, command_body_ref.nvIndex, buffer_space);
unsafe {
session_header = std::mem::zeroed();
}
session_header.session_handle = TPM_RS_PW;
marshal_session_header(&mut buffer, &session_header, buffer_space);
kv_set(keys::TPM_TAG, TPM_ST_SESSIONS);
}
pub fn marshal_startup(
mut buffer: *mut u8,
command_body: *mut tpm2_startup_cmd,
buffer_space: &mut i32,
) {
let command_body_ref;
unsafe {
command_body_ref = &*command_body;
}
kv_set(keys::TPM_TAG, TPM_ST_NO_SESSIONS);
marshal_TPM_SU(&mut buffer, command_body_ref.startup_type, buffer_space);
}
pub fn marshal_shutdown(
mut buffer: *mut u8,
command_body: *mut tpm2_shutdown_cmd,
buffer_space: &mut i32,
) {
let command_body_ref;
unsafe {
command_body_ref = &*command_body;
}
kv_set(keys::TPM_TAG, TPM_ST_NO_SESSIONS);
marshal_TPM_SU(&mut buffer, command_body_ref.shutdown_type, buffer_space);
}
pub fn tpm_marshal_command(
command: crate::tlcl::tpm20::constants::TPM_CC,
tpm_command_body: *const core::ffi::c_void,
buffer: &mut [u8; 2048],
buffer_size: usize,
) -> i32 {
let cmd_body =
unsafe { (buffer.as_mut_ptr() as *mut u8).add(core::mem::size_of::<tpm_header>()) };
let max_body_size: i32 = (buffer_size - core::mem::size_of::<tpm_header>())
.try_into()
.unwrap();
let mut body_size = max_body_size;
kv_set(keys::TPM_TAG, TPM_ST_NO_SESSIONS);
match command {
TPM2_Clear => {
marshal_clear(cmd_body, tpm_command_body, &mut body_size);
}
TPM2_NV_Read => marshal_nv_read(
cmd_body,
tpm_command_body as *mut tpm2_nv_read_cmd,
&mut body_size,
),
TPM2_NV_Write => marshal_nv_write(
cmd_body,
tpm_command_body as *mut tpm2_nv_write_cmd,
&mut body_size,
),
TPM2_NV_DefineSpace => marshal_nv_define_space(
cmd_body,
tpm_command_body as *mut tpm2_nv_define_space_cmd,
&mut body_size,
),
TPM2_NV_UndefineSpace => marshal_nv_undefine_space(
cmd_body,
tpm_command_body as *mut tpm2_nv_undefine_space_cmd,
&mut body_size,
),
TPM2_NV_ReadPublic => marshal_nv_read_public(
cmd_body,
tpm_command_body as *mut tpm2_nv_read_public_cmd,
&mut body_size,
),
TPM2_Startup => marshal_startup(
cmd_body,
tpm_command_body as *mut tpm2_startup_cmd,
&mut body_size,
),
TPM2_Shutdown => marshal_shutdown(
cmd_body,
tpm_command_body as *mut tpm2_shutdown_cmd,
&mut body_size,
),
_ => {
body_size = -1;
}
}
if body_size > 0 {
let mut header_space = core::mem::size_of::<tpm_header>() as i32;
let mut header = buffer.as_mut_ptr();
body_size = max_body_size - body_size;
body_size += core::mem::size_of::<tpm_header>() as i32;
marshal_u16(
&mut header,
match kv_get(key_types::INT, keys::TPM_TAG) {
Some(KvValue::Int(v)) => v as u16,
_ => TPM_ST_NO_SESSIONS,
},
&mut header_space,
);
marshal_u32(&mut header, body_size as u32, &mut header_space);
marshal_u32(&mut header, command, &mut header_space);
}
body_size
}
----- END ./src/tlcl/tpm20/marshal.rs -----
----- ./src/tlcl/tpm12/constants.rs -----
// Copyright 2016 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Some TPM constants and type definitions for standalone compilation for use
// in the firmware
#![allow(non_camel_case_types)]
#![allow(non_snake_case, non_upper_case_globals)]
pub const TPM_MAX_COMMAND_SIZE: u32 = 4096;
pub const TPM_LARGE_ENOUGH_COMMAND_SIZE: u32 = 256; /* saves space in the firmware */
pub const TPM_PUBEK_SIZE: u32 = 256;
pub const TPM_PCR_DIGEST: u32 = 20;
pub const TPM_NV_INDEX0: u32 = 0x00000000u32;
pub const TPM_NV_INDEX_LOCK: u32 = 0xffffffffu32;
pub const TPM_NV_INDEX_TRIAL: u32 = 0x0000f004u32;
pub const TPM_NV_PER_READ_STCLEAR: u32 = 1u32 << 31;
pub const TPM_NV_PER_AUTHREAD: u32 = 1u32 << 18;
pub const TPM_NV_PER_OWNERREAD: u32 = 1u32 << 17;
pub const TPM_NV_PER_PPREAD: u32 = 1u32 << 16;
pub const TPM_NV_PER_GLOBALLOCK: u32 = 1u32 << 15;
pub const TPM_NV_PER_WRITE_STCLEAR: u32 = 1u32 << 14;
pub const TPM_NV_PER_WRITEDEFINE: u32 = 1u32 << 13;
pub const TPM_NV_PER_WRITEALL: u32 = 1u32 << 12;
pub const TPM_NV_PER_AUTHWRITE: u32 = 1u32 << 2;
pub const TPM_NV_PER_OWNERWRITE: u32 = 1u32 << 1;
pub const TPM_NV_PER_PPWRITE: u32 = 1u32 << 0;
pub const TPM_TAG_NV_ATTRIBUTES: u16 = 0x0017u16;
pub const TPM_TAG_NV_DATA_PUBLIC: u16 = 0x0018u16;
pub const TPM_TAG_KEY12: u16 = 0x0028u16;
pub const TPM_TAG_RQU_COMMAND: u16 = 0xc1u16;
pub const TPM_TAG_RQU_AUTH1_COMMAND: u16 = 0xc2u16;
pub const TPM_TAG_RQU_AUTH2_COMMAND: u16 = 0xc3u16;
pub const TPM_TAG_RSP_COMMAND: u16 = 0xc4u16;
pub const TPM_TAG_RSP_AUTH1_COMMAND: u16 = 0xc5u16;
pub const TPM_TAG_RSP_AUTH2_COMMAND: u16 = 0xc6u16;
pub type TSS_BOOL = u8;
pub type TPM_BOOL = u8;
pub type TPM_TAG = u16;
pub type TPM_STRUCTURE_TAG = u16;
pub type TPM_NV_INDEX = u32;
pub type TPM_NV_PER_ATTRIBUTES = u32;
pub type TPM_LOCALITY_SELECTION = u8;
pub type TPM_COMMAND_CODE = u32;
pub type TPM_PHYSICAL_PRESENCE = u16;
pub type TPM_STARTUP_TYPE = u16;
pub type TPM_CAPABILITY_AREA = u32;
pub type TPM_FAMILY_LABEL = u8;
pub type TPM_FAMILY_ID = u32;
pub type TPM_FAMILY_VERIFICATION = u32;
pub type TPM_FAMILY_FLAGS = u32;
pub const TPM_CAP_FLAG: u32 = 0x00000004u32;
pub const TPM_CAP_FLAG_PERMANENT: u32 = 0x00000108u32;
pub const TPM_CAP_FLAG_VOLATILE: u32 = 0x00000109u32;
pub const TPM_CAP_PROPERTY: u32 = 0x00000005u32;
pub const TPM_CAP_PROP_OWNER: u32 = 0x00000111u32;
pub const TPM_CAP_NV_INDEX: u32 = 0x00000011u32;
pub const TPM_CAP_GET_VERSION_VAL: u32 = 0x0000001au32;
pub const TPM_AUTH_ALWAYS: u8 = 0x01u8;
pub const TPM_KEY_USAGE_STORAGE: u16 = 0x0011u16;
pub const TPM_ALG_RSA: u16 = 0x0001u16;
pub const TPM_ES_RSAESOAEP_SHA1_MGF1: u16 = 0x0003u16;
pub const TPM_SS_NONE: u16 = 0x0001u16;
pub const TPM_PID_OWNER: u16 = 0x0005u16;
pub const TPM_ET_OWNER: u32 = 0x02u32;
pub const TPM_FAMILY_CREATE: u32 = 0x00000001u32;
pub const TPM_ST_CLEAR: u16 = 0x0001u16;
pub const TPM_ST_STATE: u16 = 0x0002u16;
pub const TPM_ST_DEACTIVATED: u16 = 0x0003u16;
pub const TPM_LOC_FOUR: u32 = 1u32 << 4;
pub const TPM_LOC_THREE: u32 = 1u32 << 3;
pub const TPM_LOC_TWO: u32 = 1u32 << 2;
pub const TPM_LOC_ONE: u32 = 1u32 << 1;
pub const TPM_LOC_ZERO: u32 = 1u32 << 0;
pub const TPM_ALL_LOCALITIES: u32 =
TPM_LOC_ZERO | TPM_LOC_ONE | TPM_LOC_TWO | TPM_LOC_THREE | TPM_LOC_FOUR; /* 0x1f */
pub const TPM_PHYSICAL_PRESENCE_LOCK: u16 = 0x0004u16;
pub const TPM_PHYSICAL_PRESENCE_PRESENT: u16 = 0x0008u16;
pub const TPM_PHYSICAL_PRESENCE_NOTPRESENT: u16 = 0x0010u16;
pub const TPM_PHYSICAL_PRESENCE_CMD_ENABLE: u16 = 0x0020u16;
pub const TPM_PHYSICAL_PRESENCE_HW_ENABLE: u16 = 0x0040u16;
pub const TPM_PHYSICAL_PRESENCE_LIFETIME_LOCK: u16 = 0x0080u16;
pub const TPM_PHYSICAL_PRESENCE_CMD_DISABLE: u16 = 0x0100u16;
pub const TPM_PHYSICAL_PRESENCE_HW_DISABLE: u16 = 0x0200u16;
pub const TPM_SHA1_160_HASH_LEN: usize = 0x14;
pub const TPM_SHA1BASED_NONCE_LEN: usize = TPM_SHA1_160_HASH_LEN;
pub const TPM_AUTH_DATA_LEN: usize = 0x14;
pub const TPM_RSA_2048_LEN: usize = 0x100;
#[derive(Clone, Copy)]
#[repr(C, packed)]
pub struct TPM_DIGEST {
pub digest: [u8; TPM_SHA1_160_HASH_LEN],
}
pub type TPM_COMPOSITE_HASH = TPM_DIGEST;
#[derive(Clone, Copy)]
#[repr(C, packed)]
pub struct TPM_PCR_SELECTION {
pub sizeOfSelect: u16,
pub pcrSelect: [u8; 3],
}
#[derive(Clone, Copy)]
#[repr(C, packed)]
pub struct TPM_NV_ATTRIBUTES {
pub tag: TPM_STRUCTURE_TAG,
pub attributes: TPM_NV_PER_ATTRIBUTES,
}
#[derive(Clone, Copy)]
#[repr(C, packed)]
pub struct TPM_PCR_INFO_SHORT {
pub pcrSelection: TPM_PCR_SELECTION,
pub localityAtRelease: TPM_LOCALITY_SELECTION,
pub digestAtRelease: TPM_COMPOSITE_HASH,
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct TPM_PERMANENT_FLAGS {
pub tag: TPM_STRUCTURE_TAG,
pub disable: TSS_BOOL,
pub ownership: TSS_BOOL,
pub deactivated: TSS_BOOL,
pub readPubek: TSS_BOOL,
pub disableOwnerClear: TSS_BOOL,
pub allowMaintenance: TSS_BOOL,
pub physicalPresenceLifetimeLock: TSS_BOOL,
pub physicalPresenceHWEnable: TSS_BOOL,
pub physicalPresenceCMDEnable: TSS_BOOL,
pub CEKPUsed: TSS_BOOL,
pub TPMpost: TSS_BOOL,
pub TPMpostLock: TSS_BOOL,
pub FIPS: TSS_BOOL,
pub Operator: TSS_BOOL,
pub enableRevokeEK: TSS_BOOL,
pub nvLocked: TSS_BOOL,
pub readSRKPub: TSS_BOOL,
pub tpmEstablished: TSS_BOOL,
pub maintenanceDone: TSS_BOOL,
pub disableFullDALogicInfo: TSS_BOOL,
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct TPM_STCLEAR_FLAGS {
pub tag: TPM_STRUCTURE_TAG,
pub deactivated: TSS_BOOL,
pub disableForceClear: TSS_BOOL,
pub physicalPresence: TSS_BOOL,
pub physicalPresenceLock: TSS_BOOL,
pub bGlobalLock: TSS_BOOL,
}
#[derive(Clone, Copy)]
#[repr(C, packed)]
pub struct TPM_NV_DATA_PUBLIC {
pub tag: TPM_STRUCTURE_TAG,
pub nvIndex: TPM_NV_INDEX,
pub pcrInfoRead: TPM_PCR_INFO_SHORT,
pub pcrInfoWrite: TPM_PCR_INFO_SHORT,
pub permission: TPM_NV_ATTRIBUTES,
pub bReadSTClear: TPM_BOOL,
pub bWriteSTClear: TPM_BOOL,
pub bWriteDefine: TPM_BOOL,
pub dataSize: u32,
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct TPM_NONCE {
pub nonce: [u8; TPM_SHA1BASED_NONCE_LEN],
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct TPM_FAMILY_TABLE_ENTRY {
pub tag: TPM_STRUCTURE_TAG,
pub familyLabel: TPM_FAMILY_LABEL,
pub familyID: TPM_FAMILY_ID,
pub verificationCount: TPM_FAMILY_VERIFICATION,
pub flags: TPM_FAMILY_FLAGS,
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct TPM_IFX_FIRMWAREPACKAGE {
pub FwPackageIdentifier: u32,
pub Version: u32,
pub StaleVersion: u32,
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct TPM_IFX_FIELDUPGRADEINFO {
pub wMaxDataSize: u16,
pub sBootloaderFirmwarePackage: TPM_IFX_FIRMWAREPACKAGE,
pub sFirmwarePackages: [TPM_IFX_FIRMWAREPACKAGE; 2],
pub wSecurityModuleStatus: u16,
pub sProcessFirmwarePackage: TPM_IFX_FIRMWAREPACKAGE,
pub wFieldUpgradeCounter: u16,
}
#[derive(Clone, Copy)]
#[repr(C, packed)]
pub struct TPM_NV_AUTH_POLICY {
pub pcr_info_read: TPM_PCR_INFO_SHORT,
pub pcr_info_write: TPM_PCR_INFO_SHORT,
}
pub const TPM_IFX_FieldUpgradeInfoRequest2: u8 = 0x11u8;
/* Ordinals */
pub const TPM_ORD_ContinueSelfTest: u32 = 0x00000053u32;
pub const TPM_ORD_Delegate_Manage: u32 = 0x000000D2u32;
pub const TPM_ORD_Delegate_ReadTable: u32 = 0x000000DBu32;
pub const TPM_ORD_Extend: u32 = 0x00000014u32;
pub const TPM_ORD_FieldUpgrade: u32 = 0x000000AAu32;
pub const TPM_ORD_ForceClear: u32 = 0x0000005Du32;
pub const TPM_ORD_GetCapability: u32 = 0x00000065u32;
pub const TPM_ORD_GetRandom: u32 = 0x00000046u32;
pub const TPM_ORD_NV_DefineSpace: u32 = 0x000000CCu32;
pub const TPM_ORD_NV_ReadValue: u32 = 0x000000CFu32;
pub const TPM_ORD_NV_WriteValue: u32 = 0x000000CDu32;
pub const TPM_ORD_OIAP: u32 = 0x0000000Au32;
pub const TPM_ORD_OSAP: u32 = 0x0000000Bu32;
pub const TPM_ORD_PcrRead: u32 = 0x00000015u32;
pub const TPM_ORD_PhysicalEnable: u32 = 0x0000006Fu32;
pub const TPM_ORD_PhysicalDisable: u32 = 0x00000070u32;
pub const TSC_ORD_PhysicalPresence: u32 = 0x4000000Au32;
pub const TPM_ORD_PhysicalSetDeactivated: u32 = 0x00000072u32;
pub const TPM_ORD_ReadPubek: u32 = 0x0000007Cu32;
pub const TPM_ORD_SaveState: u32 = 0x00000098u32;
pub const TPM_ORD_SelfTestFull: u32 = 0x00000050u32;
pub const TPM_ORD_Startup: u32 = 0x00000099u32;
pub const TPM_ORD_TakeOwnership: u32 = 0x0000000Du32;
pub const TPM_SUCCESS: u32 = 0x00000000;
pub const TPM_BUFFER_SIZE: usize = 2048;
pub const TPM_CMD_HEADER_SIZE: usize = 10;
pub const TPM_RESP_HEADER_SIZE: usize = 10;
pub type TPM_COMMAND = u32;
#[repr(C)]
pub struct tpm1_nv_read_cmd {
pub nvIndex: u32,
pub offset: u32,
pub size: u32,
}
#[repr(C)]
pub struct tpm1_nv_write_cmd {
pub nvIndex: u32,
pub offset: u32,
pub size: u32,
pub data: *const u8,
}
#[repr(C)]
pub struct tpm1_nv_define_space_cmd {
pub nvIndex: u32,
pub perm: u32,
pub size: u32,
pub auth_policy: *const TPM_NV_AUTH_POLICY,
}
#[repr(C)]
pub struct tpm1_physical_presence_cmd {
pub physical_presence: u16,
}
#[repr(C)]
pub struct tpm1_startup_cmd {
pub startup_type: u16,
}
#[repr(C, packed)]
pub struct tpm_header {
pub tpm_tag: u16,
pub tpm_size: u32,
pub tpm_code: u32,
}
#[repr(C)]
pub struct nv_read_response {
pub data_size: u32,
pub data: [u8; TPM_BUFFER_SIZE],
}
#[repr(C)]
pub struct tpm1_response {
pub hdr: tpm_header,
pub nvr: nv_read_response,
}
----- END ./src/tlcl/tpm12/constants.rs -----
----- ./src/tlcl/tpm12/mod.rs -----
pub mod constants;
pub mod marshal;
pub mod unmarshal;
pub mod utils;
pub fn tpm_get_response(
command: constants::TPM_COMMAND,
command_body: *const core::ffi::c_void,
response: &mut constants::tpm1_response,
) -> u32 {
crate::tlcl::backend::tpm_get_response::<{ constants::TPM_BUFFER_SIZE }, _, _, _, _>(
command,
command_body,
response,
marshal::tpm_marshal_command,
unmarshal::tpm_unmarshal_response,
|resp| unsafe { core::ptr::read_unaligned(core::ptr::addr_of!(resp.hdr.tpm_code)) },
)
}
pub fn tpm_send_receive(
command: constants::TPM_COMMAND,
command_body: *const core::ffi::c_void,
response: &mut constants::tpm1_response,
) -> u32 {
let rv = tpm_get_response(command, command_body, response);
if rv != crate::tlcl::constants::TPM_SUCCESS {
return rv;
}
unsafe { core::ptr::read_unaligned(core::ptr::addr_of!(response.hdr.tpm_code)) }
}
pub fn tpm_get_response_code(
command: constants::TPM_COMMAND,
command_body: *const core::ffi::c_void,
) -> u32 {
let mut response: constants::tpm1_response = unsafe { core::mem::zeroed() };
tpm_send_receive(command, command_body, &mut response)
}
----- END ./src/tlcl/tpm12/mod.rs -----
----- ./src/tlcl/tpm12/utils.rs -----
#![allow(non_snake_case)]
pub use crate::tlcl::bytes::{read_be16, read_be32, write_be16, write_be32};
use crate::tlcl::tpm12::constants::{
TPM_ALL_LOCALITIES, TPM_DIGEST, TPM_LOC_THREE, TPM_NV_AUTH_POLICY, TPM_PCR_INFO_SHORT,
TPM_PCR_SELECTION,
};
const EMPTY_PCR_SELECTION_SHA1: [u8; 20] = [
0x79, 0xdd, 0xda, 0xfd, 0xc1, 0x97, 0xdc, 0xcc, 0xe9, 0x98, 0x9a, 0xee, 0xf5, 0x52, 0x89, 0xee,
0x24, 0x96, 0x4c, 0xac,
];
pub fn decode_pcr_info(
response: &[u8],
cursor: &mut usize,
end: usize,
pcr_info: *mut TPM_PCR_INFO_SHORT,
) -> bool {
if end.saturating_sub(*cursor) < core::mem::size_of::<u16>() {
return false;
}
let size_of_select = read_be16(unsafe { response.as_ptr().add(*cursor) });
let encoded_size = core::mem::size_of::<TPM_PCR_INFO_SHORT>() - 3 + size_of_select as usize;
if end.saturating_sub(*cursor) < encoded_size || size_of_select as usize > 3 {
return false;
}
let mut pcr_select = [0u8; 3];
let select_start = *cursor + core::mem::size_of::<u16>();
pcr_select[..size_of_select as usize]
.copy_from_slice(&response[select_start..select_start + size_of_select as usize]);
*cursor += core::mem::size_of::<u16>() + size_of_select as usize;
if end.saturating_sub(*cursor) < 1 + core::mem::size_of::<TPM_DIGEST>() {
return false;
}
let locality = response[*cursor];
*cursor += 1;
let mut digest = [0u8; core::mem::size_of::<TPM_DIGEST>()];
digest.copy_from_slice(&response[*cursor..*cursor + core::mem::size_of::<TPM_DIGEST>()]);
*cursor += core::mem::size_of::<TPM_DIGEST>();
let info = TPM_PCR_INFO_SHORT {
pcrSelection: TPM_PCR_SELECTION {
sizeOfSelect: size_of_select,
pcrSelect: pcr_select,
},
localityAtRelease: locality,
digestAtRelease: TPM_DIGEST { digest },
};
unsafe {
pcr_info.write_unaligned(info);
}
true
}
pub fn init_default_nv_auth_policy(policy: *mut TPM_NV_AUTH_POLICY) {
let pcr_info = TPM_PCR_INFO_SHORT {
pcrSelection: TPM_PCR_SELECTION {
sizeOfSelect: u16::from_be(3),
pcrSelect: [0, 0, 0],
},
localityAtRelease: (TPM_ALL_LOCALITIES & !TPM_LOC_THREE) as u8,
digestAtRelease: TPM_DIGEST {
digest: EMPTY_PCR_SELECTION_SHA1,
},
};
unsafe {
core::ptr::addr_of_mut!((*policy).pcr_info_read).write_unaligned(pcr_info);
core::ptr::addr_of_mut!((*policy).pcr_info_write).write_unaligned(pcr_info);
}
}
pub fn init_define_space_default_auth_policy(policy: *mut TPM_NV_AUTH_POLICY) {
let pcr_info = TPM_PCR_INFO_SHORT {
pcrSelection: TPM_PCR_SELECTION {
sizeOfSelect: u16::from_be(3),
pcrSelect: [0, 0, 0],
},
localityAtRelease: TPM_ALL_LOCALITIES as u8,
digestAtRelease: TPM_DIGEST { digest: [0u8; 20] },
};
unsafe {
core::ptr::addr_of_mut!((*policy).pcr_info_read).write_unaligned(pcr_info);
core::ptr::addr_of_mut!((*policy).pcr_info_write).write_unaligned(pcr_info);
}
}
----- END ./src/tlcl/tpm12/utils.rs -----
----- ./src/tlcl/tpm12/unmarshal.rs -----
#![allow(non_upper_case_globals)]
#![allow(non_snake_case)]
use crate::tlcl::{
bytes::{read_be16, read_be32},
tpm12::constants::{
TPM_COMMAND, TPM_ORD_NV_ReadValue, TPM_RESP_HEADER_SIZE, TPM_SUCCESS, TPM_TAG_RSP_COMMAND,
nv_read_response, tpm1_response,
},
};
pub fn unmarshal_u16(buffer: &mut *const u8, buffer_space: &mut i32) -> u16 {
let value: u16;
if *buffer_space < core::mem::size_of::<u16>() as i32 {
*buffer_space = -1;
return 0;
}
value = read_be16(*buffer);
unsafe {
*buffer = (*buffer).add(core::mem::size_of::<u16>());
}
*buffer_space -= core::mem::size_of::<u16>() as i32;
value
}
pub fn unmarshal_u32(buffer: &mut *const u8, buffer_space: &mut i32) -> u32 {
let value: u32;
if *buffer_space < core::mem::size_of::<u32>() as i32 {
*buffer_space = -1;
return 0;
}
value = read_be32(*buffer);
unsafe {
*buffer = (*buffer).add(core::mem::size_of::<u32>());
}
*buffer_space -= core::mem::size_of::<u32>() as i32;
value
}
pub fn unmarshal_nv_read(buffer: &mut *const u8, size: &mut i32, nvr: &mut nv_read_response) {
nvr.data_size = unmarshal_u32(buffer, size);
if nvr.data_size as i32 > *size {
*size = -1;
nvr.data_size = 0;
return;
}
if nvr.data_size as usize > nvr.data.len() {
*size = -1;
nvr.data_size = 0;
return;
}
unsafe {
core::ptr::copy_nonoverlapping(*buffer, nvr.data.as_mut_ptr(), nvr.data_size as usize);
*buffer = (*buffer).add(nvr.data_size as usize);
}
*size -= nvr.data_size as i32;
}
pub fn tpm_unmarshal_response(
command: TPM_COMMAND,
response_body: *const core::ffi::c_void,
mut cr_size: i32,
response: &mut tpm1_response,
) -> i32 {
let mut buffer = response_body as *const u8;
if cr_size < TPM_RESP_HEADER_SIZE as i32 {
return -1;
}
response.hdr.tpm_tag = unmarshal_u16(&mut buffer, &mut cr_size);
response.hdr.tpm_size = unmarshal_u32(&mut buffer, &mut cr_size);
response.hdr.tpm_code = unmarshal_u32(&mut buffer, &mut cr_size);
if response.hdr.tpm_tag != TPM_TAG_RSP_COMMAND {
return -1;
}
if response.hdr.tpm_size < TPM_RESP_HEADER_SIZE as u32 {
return -1;
}
if response.hdr.tpm_code != TPM_SUCCESS {
return 0;
}
if cr_size > 0 {
match command {
TPM_ORD_NV_ReadValue => {
unmarshal_nv_read(&mut buffer, &mut cr_size, &mut response.nvr);
}
_ => {
cr_size = 0;
}
}
}
if cr_size != 0 {
return -1;
}
0
}
----- END ./src/tlcl/tpm12/unmarshal.rs -----
----- ./src/tlcl/tpm12/marshal.rs -----
#![allow(non_upper_case_globals)]
#![allow(non_snake_case)]
use crate::tlcl::{
bytes::{write_be16, write_be32},
tpm12::constants::{
TPM_BUFFER_SIZE, TPM_CMD_HEADER_SIZE, TPM_COMMAND, TPM_ORD_ForceClear, TPM_ORD_NV_DefineSpace,
TPM_ORD_NV_ReadValue, TPM_ORD_NV_WriteValue, TPM_ORD_PhysicalEnable, TPM_ORD_SaveState,
TPM_ORD_Startup, TPM_TAG_NV_ATTRIBUTES, TPM_TAG_NV_DATA_PUBLIC, TPM_TAG_RQU_COMMAND,
TSC_ORD_PhysicalPresence, tpm_header, tpm1_nv_define_space_cmd, tpm1_nv_read_cmd,
tpm1_nv_write_cmd, tpm1_physical_presence_cmd, tpm1_startup_cmd,
},
};
pub fn marshal_u16(buffer: &mut *mut u8, value: u16, buffer_space: &mut i32) {
if *buffer_space < core::mem::size_of::<u16>() as i32 {
*buffer_space = -1;
return;
}
write_be16(*buffer, value);
unsafe {
*buffer = (*buffer).add(core::mem::size_of::<u16>());
}
*buffer_space -= core::mem::size_of::<u16>() as i32;
}
pub fn marshal_u8(buffer: &mut *mut u8, value: u8, buffer_space: &mut i32) {
if *buffer_space < core::mem::size_of::<u8>() as i32 {
*buffer_space = -1;
return;
}
unsafe {
**buffer = value;
*buffer = (*buffer).add(core::mem::size_of::<u8>());
}
*buffer_space -= core::mem::size_of::<u8>() as i32;
}
pub fn marshal_u32(buffer: &mut *mut u8, value: u32, buffer_space: &mut i32) {
if *buffer_space < core::mem::size_of::<u32>() as i32 {
*buffer_space = -1;
return;
}
write_be32(*buffer, value);
unsafe {
*buffer = (*buffer).add(core::mem::size_of::<u32>());
}
*buffer_space -= core::mem::size_of::<u32>() as i32;
}
pub fn marshal_blob(
buffer: &mut *mut u8,
blob: *const u8,
blob_size: usize,
buffer_space: &mut i32,
) {
if *buffer_space < blob_size as i32 {
*buffer_space = -1;
return;
}
unsafe {
core::ptr::copy_nonoverlapping(blob, *buffer, blob_size);
*buffer = (*buffer).add(blob_size);
}
*buffer_space -= blob_size as i32;
}
pub fn marshal_force_clear(_buffer: *mut u8, _buffer_space: &mut i32) {}
pub fn marshal_physical_enable(_buffer: *mut u8, _buffer_space: &mut i32) {}
pub fn marshal_physical_presence(
mut buffer: *mut u8,
command_body: *const tpm1_physical_presence_cmd,
buffer_space: &mut i32,
) {
if command_body.is_null() {
*buffer_space = -1;
return;
}
let command_body_ref = unsafe { &*command_body };
marshal_u16(
&mut buffer,
command_body_ref.physical_presence,
buffer_space,
);
}
pub fn marshal_nv_read(
mut buffer: *mut u8,
command_body: *const tpm1_nv_read_cmd,
buffer_space: &mut i32,
) {
if command_body.is_null() {
*buffer_space = -1;
return;
}
let command_body_ref = unsafe { &*command_body };
marshal_u32(&mut buffer, command_body_ref.nvIndex, buffer_space);
marshal_u32(&mut buffer, command_body_ref.offset, buffer_space);
marshal_u32(&mut buffer, command_body_ref.size, buffer_space);
}
pub fn marshal_nv_write(
mut buffer: *mut u8,
command_body: *const tpm1_nv_write_cmd,
buffer_space: &mut i32,
) {
if command_body.is_null() {
*buffer_space = -1;
return;
}
let command_body_ref = unsafe { &*command_body };
marshal_u32(&mut buffer, command_body_ref.nvIndex, buffer_space);
marshal_u32(&mut buffer, command_body_ref.offset, buffer_space);
marshal_u32(&mut buffer, command_body_ref.size, buffer_space);
marshal_blob(
&mut buffer,
command_body_ref.data,
command_body_ref.size as usize,
buffer_space,
);
}
pub fn marshal_nv_define_space(
mut buffer: *mut u8,
command_body: *const tpm1_nv_define_space_cmd,
buffer_space: &mut i32,
) {
if command_body.is_null() {
*buffer_space = -1;
return;
}
let command_body_ref = unsafe { &*command_body };
marshal_u16(&mut buffer, TPM_TAG_NV_DATA_PUBLIC, buffer_space);
marshal_u32(&mut buffer, command_body_ref.nvIndex, buffer_space);
if command_body_ref.auth_policy.is_null() {
*buffer_space = -1;
return;
}
marshal_blob(
&mut buffer,
command_body_ref.auth_policy as *const u8,
core::mem::size_of::<crate::tlcl::tpm12::constants::TPM_NV_AUTH_POLICY>(),
buffer_space,
);
marshal_u16(&mut buffer, TPM_TAG_NV_ATTRIBUTES, buffer_space);
marshal_u32(&mut buffer, command_body_ref.perm, buffer_space);
marshal_u8(&mut buffer, 0, buffer_space);
marshal_u8(&mut buffer, 0, buffer_space);
marshal_u8(&mut buffer, 0, buffer_space);
marshal_u32(&mut buffer, command_body_ref.size, buffer_space);
marshal_u32(&mut buffer, 0, buffer_space);
marshal_u32(&mut buffer, 0, buffer_space);
marshal_u32(&mut buffer, 0, buffer_space);
marshal_u32(&mut buffer, 0, buffer_space);
marshal_u32(&mut buffer, 0, buffer_space);
}
pub fn marshal_startup(
mut buffer: *mut u8,
command_body: *const tpm1_startup_cmd,
buffer_space: &mut i32,
) {
if command_body.is_null() {
*buffer_space = -1;
return;
}
let command_body_ref = unsafe { &*command_body };
marshal_u16(&mut buffer, command_body_ref.startup_type, buffer_space);
}
pub fn marshal_savestate(_buffer: *mut u8, _buffer_space: &mut i32) {}
pub fn tpm_marshal_command(
command: TPM_COMMAND,
tpm_command_body: *const core::ffi::c_void,
buffer: &mut [u8; TPM_BUFFER_SIZE],
buffer_size: usize,
) -> i32 {
let cmd_body =
unsafe { (buffer.as_mut_ptr() as *mut u8).add(core::mem::size_of::<tpm_header>()) };
let max_body_size: i32 = (buffer_size - core::mem::size_of::<tpm_header>())
.try_into()
.unwrap();
let mut body_size = max_body_size;
match command {
TPM_ORD_ForceClear => {
marshal_force_clear(cmd_body, &mut body_size);
}
TPM_ORD_PhysicalEnable => {
marshal_physical_enable(cmd_body, &mut body_size);
}
TSC_ORD_PhysicalPresence => {
marshal_physical_presence(
cmd_body,
tpm_command_body as *const tpm1_physical_presence_cmd,
&mut body_size,
);
}
TPM_ORD_NV_ReadValue => {
marshal_nv_read(
cmd_body,
tpm_command_body as *const tpm1_nv_read_cmd,
&mut body_size,
);
}
TPM_ORD_NV_WriteValue => {
marshal_nv_write(
cmd_body,
tpm_command_body as *const tpm1_nv_write_cmd,
&mut body_size,
);
}
TPM_ORD_NV_DefineSpace => {
marshal_nv_define_space(
cmd_body,
tpm_command_body as *const tpm1_nv_define_space_cmd,
&mut body_size,
);
}
TPM_ORD_Startup => {
marshal_startup(
cmd_body,
tpm_command_body as *const tpm1_startup_cmd,
&mut body_size,
);
}
TPM_ORD_SaveState => {
marshal_savestate(cmd_body, &mut body_size);
}
_ => {
body_size = -1;
}
}
if body_size > 0 {
let mut header_space = core::mem::size_of::<tpm_header>() as i32;
let mut header = buffer.as_mut_ptr();
body_size = max_body_size - body_size;
body_size += TPM_CMD_HEADER_SIZE as i32;
marshal_u16(&mut header, TPM_TAG_RQU_COMMAND, &mut header_space);
marshal_u32(&mut header, body_size as u32, &mut header_space);
marshal_u32(&mut header, command, &mut header_space);
}
body_size
}
----- END ./src/tlcl/tpm12/marshal.rs -----
----- ./src/tlcl/stubs/constants.rs -----
#![allow(non_camel_case_types)]
pub type TPM_COMMAND = u32;
pub type TPM_CC = u32;
#[derive(Copy, Clone)]
pub struct tpm_header {
pub tpm_code: u32,
}
#[derive(Copy, Clone)]
pub struct tpm1_response {
pub hdr: tpm_header,
}
#[derive(Copy, Clone)]
pub struct tpm2_response {
pub hdr: tpm_header,
}
pub const TPM_BUFFER_SIZE: usize = 2048;
----- END ./src/tlcl/stubs/constants.rs -----
----- ./src/tlcl/stubs/mod.rs -----
pub mod constants;
pub mod marshal;
pub mod unmarshal;
pub fn tpm_get_response(
_command: u32,
_command_body: *const core::ffi::c_void,
_response: *mut core::ffi::c_void,
) -> u32 {
crate::tlcl::constants::TPM_E_NO_SUCH_COMMAND
}
pub fn tpm_send_receive(
_command: u32,
_command_body: *const core::ffi::c_void,
_response: *mut core::ffi::c_void,
) -> u32 {
crate::tlcl::constants::TPM_E_NO_SUCH_COMMAND
}
pub fn tpm_get_response_code(_command: u32, _command_body: *const core::ffi::c_void) -> u32 {
crate::tlcl::constants::TPM_E_NO_SUCH_COMMAND
}
----- END ./src/tlcl/stubs/mod.rs -----
----- ./src/tlcl/stubs/unmarshal.rs -----
pub fn tpm_unmarshal_response(
_command: u32,
_buffer: *const core::ffi::c_void,
_buffer_size: i32,
_response: *mut core::ffi::c_void,
) -> i32 {
-1
}
----- END ./src/tlcl/stubs/unmarshal.rs -----
----- ./src/tlcl/stubs/marshal.rs -----
pub fn tpm_marshal_command(
_command: u32,
_command_body: *const core::ffi::c_void,
_buffer: &mut [u8],
_buffer_size: usize,
) -> i32 {
-1
}
----- END ./src/tlcl/stubs/marshal.rs -----
----- ./src/tlcl/permissions.rs -----
#[cfg(all(feature = "tpm1_2", not(feature = "tpm2_0")))]
pub use crate::tlcl::tpm12::constants::{
TPM_NV_PER_AUTHREAD as NV_PERM_AUTHREAD, TPM_NV_PER_AUTHWRITE as NV_PERM_AUTHWRITE,
TPM_NV_PER_GLOBALLOCK as NV_PERM_GLOBALLOCK, TPM_NV_PER_OWNERREAD as NV_PERM_OWNERREAD,
TPM_NV_PER_OWNERWRITE as NV_PERM_OWNERWRITE, TPM_NV_PER_PPREAD as NV_PERM_PPREAD,
TPM_NV_PER_PPWRITE as NV_PERM_PPWRITE, TPM_NV_PER_READ_STCLEAR as NV_PERM_READ_STCLEAR,
TPM_NV_PER_WRITE_STCLEAR as NV_PERM_WRITE_STCLEAR, TPM_NV_PER_WRITEDEFINE as NV_PERM_WRITEDEFINE,
};
#[cfg(all(feature = "tpm2_0", not(feature = "tpm1_2")))]
pub use crate::tlcl::tpm20::constants::{
TPMA_NV_AUTHREAD as NV_PERM_AUTHREAD, TPMA_NV_AUTHWRITE as NV_PERM_AUTHWRITE,
TPMA_NV_OWNERREAD as NV_PERM_OWNERREAD, TPMA_NV_OWNERWRITE as NV_PERM_OWNERWRITE,
TPMA_NV_PLATFORMCREATE as NV_PERM_PLATFORMCREATE, TPMA_NV_PPREAD as NV_PERM_PPREAD,
TPMA_NV_PPWRITE as NV_PERM_PPWRITE, TPMA_NV_READ_STCLEAR as NV_PERM_READ_STCLEAR,
TPMA_NV_WRITE_STCLEAR as NV_PERM_WRITE_STCLEAR, TPMA_NV_WRITEDEFINE as NV_PERM_WRITEDEFINE,
};
#[cfg(all(feature = "tpm1_2", feature = "tpm2_0"))]
pub use crate::tlcl::tpm20::constants::{
TPMA_NV_AUTHREAD as NV_PERM_AUTHREAD, TPMA_NV_AUTHWRITE as NV_PERM_AUTHWRITE,
TPMA_NV_OWNERREAD as NV_PERM_OWNERREAD, TPMA_NV_OWNERWRITE as NV_PERM_OWNERWRITE,
TPMA_NV_PPREAD as NV_PERM_PPREAD, TPMA_NV_PPWRITE as NV_PERM_PPWRITE,
TPMA_NV_READ_STCLEAR as NV_PERM_READ_STCLEAR, TPMA_NV_WRITE_STCLEAR as NV_PERM_WRITE_STCLEAR,
TPMA_NV_WRITEDEFINE as NV_PERM_WRITEDEFINE,
};
#[cfg(feature = "tpm1_2")]
pub mod tpm12 {
pub use crate::tlcl::tpm12::constants::{
TPM_NV_PER_AUTHREAD as NV_PERM_AUTHREAD, TPM_NV_PER_AUTHWRITE as NV_PERM_AUTHWRITE,
TPM_NV_PER_GLOBALLOCK as NV_PERM_GLOBALLOCK, TPM_NV_PER_OWNERREAD as NV_PERM_OWNERREAD,
TPM_NV_PER_OWNERWRITE as NV_PERM_OWNERWRITE, TPM_NV_PER_PPREAD as NV_PERM_PPREAD,
TPM_NV_PER_PPWRITE as NV_PERM_PPWRITE, TPM_NV_PER_READ_STCLEAR as NV_PERM_READ_STCLEAR,
TPM_NV_PER_WRITE_STCLEAR as NV_PERM_WRITE_STCLEAR,
TPM_NV_PER_WRITEDEFINE as NV_PERM_WRITEDEFINE,
};
}
#[cfg(feature = "tpm2_0")]
pub mod tpm20 {
pub use crate::tlcl::tpm20::constants::{
TPMA_NV_AUTHREAD as NV_PERM_AUTHREAD, TPMA_NV_AUTHWRITE as NV_PERM_AUTHWRITE,
TPMA_NV_OWNERREAD as NV_PERM_OWNERREAD, TPMA_NV_OWNERWRITE as NV_PERM_OWNERWRITE,
TPMA_NV_PLATFORMCREATE as NV_PERM_PLATFORMCREATE, TPMA_NV_PPREAD as NV_PERM_PPREAD,
TPMA_NV_PPWRITE as NV_PERM_PPWRITE, TPMA_NV_READ_STCLEAR as NV_PERM_READ_STCLEAR,
TPMA_NV_WRITE_STCLEAR as NV_PERM_WRITE_STCLEAR, TPMA_NV_WRITEDEFINE as NV_PERM_WRITEDEFINE,
};
}
----- END ./src/tlcl/permissions.rs -----
----- ./.github/workflows/tlcl_examples.yaml -----
name: tlcl_examples
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
env:
CARGO_TERM_COLOR: always
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example: [tlcl_clear, tlcl_read, tlcl_read_with_offset, tlcl_write, tlcl_write_with_offset, tlcl_define_and_undefine, tlcl_startup, tlcl_save_state, tlcl_resume]
tpm_version: [tpm1_2, tpm2_0]
steps:
- uses: actions/checkout@v4
- name: install dependencies
run: |
sudo apt-get update
sudo apt-get install -y swtpm swtpm-tools tmux
- name: prepare vTPM proxy
id: vtpm
run: |
sudo bash scripts/build_vtpm.sh
if [[ ! -e /dev/vtpmx ]]; then
sudo modprobe tpm_vtpm_proxy || true
fi
if [[ ! -e /dev/vtpmx ]]; then
echo "/dev/vtpmx is unavailable on this runner"
echo "swtpm --vtpm-proxy needs tpm_vtpm_proxy"
echo "available=false" >> "$GITHUB_OUTPUT"
exit 0
fi
# this seems jank ngl...
echo "available=true" >> "$GITHUB_OUTPUT"
- name: start tpm simulator
if: steps.vtpm.outputs.available == 'true'
run: |
TPM_LOG="$RUNNER_TEMP/tpm-${{ matrix.tpm_version }}.log"
if [[ "${{ matrix.tpm_version }}" == "tpm1_2" ]]; then
tmux new-session -d -s tpm-sim "bash \"$GITHUB_WORKSPACE/scripts/start_tpm1.sh\" > \"$TPM_LOG\" 2>&1"
else
tmux new-session -d -s tpm-sim "bash \"$GITHUB_WORKSPACE/scripts/start_tpm2.sh\" > \"$TPM_LOG\" 2>&1"
fi
for _ in $(seq 1 30); do
if compgen -G '/dev/tpm[0-9]*' > /dev/null; then
break
fi
sleep 1
done
if ! compgen -G '/dev/tpm[0-9]*' > /dev/null; then
echo "no tpm dev found after waiting"
echo "--| START TPM LOG |--"
cat "$TPM_LOG"
echo "--| END TPM LOG |--"
exit 1
fi
- name: prep tpm with required NV space(s)
if: steps.vtpm.outputs.available == 'true'
run: |
TPM_DEV=$(ls /dev/tpm[0-9]* 2>/dev/null | sort -V | tail -n 1)
if [[ -z "$TPM_DEV" ]]; then
echo "no tpm found in /dev."
tmux capture-pane -pt tpm-sim || true
exit 1
fi
echo "tpm dev: $TPM_DEV"
cargo build --example tlcl_kernver --features "example tlcl ${{ matrix.tpm_version }}"
sudo ./target/debug/examples/tlcl_kernver --tpm-path "$TPM_DEV" --verbose
- name: run example
if: steps.vtpm.outputs.available == 'true'
run: |
TPM_DEV=$(ls /dev/tpm[0-9]* 2>/dev/null | sort -V | tail -n 1)
if [[ -z "$TPM_DEV" ]]; then
echo "no tpm found in /dev."
tmux capture-pane -pt tpm-sim || true
exit 1
fi
echo "tpm dev: $TPM_DEV"
TPM_PATH="$TPM_DEV" cargo build --example ${{ matrix.example }} \
--features "example tlcl ${{ matrix.tpm_version }}"
sudo ./target/debug/examples/${{ matrix.example }} --tpm-path "$TPM_DEV" --verbose
- name: explain skip
if: steps.vtpm.outputs.available != 'true'
run: |
echo "skipping because /dev/vtpmx is unavailable on this runner"
----- END ./.github/workflows/tlcl_examples.yaml -----
----- ./.github/workflows/libargs.yaml -----
name: libargs
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: run example
run: |
cargo build --example ${{ github.workflow }} --features "example"
./target/debug/examples/${{ github.workflow }}
./target/debug/examples/${{ github.workflow }} --foobar=1234
./target/debug/examples/${{ github.workflow }} --foobar 1234
./target/debug/examples/${{ github.workflow }} --foobar=1234 --verbose
./target/debug/examples/${{ github.workflow }} --foobar 1234 --verbose
./target/debug/examples/${{ github.workflow }} --verbose
./target/debug/examples/${{ github.workflow }} --help
./target/debug/examples/${{ github.workflow }} --foobar=1234 --help
./target/debug/examples/${{ github.workflow }} --foobar 1234 --help
./target/debug/examples/${{ github.workflow }} --foobar=1234 --verbose --help
./target/debug/examples/${{ github.workflow }} --foobar 1234 --verbose --help
----- END ./.github/workflows/libargs.yaml -----
----- ./.github/workflows/logger.yaml -----
name: logger
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: run example
run: |
cargo build --example ${{ github.workflow }} --features "example"
./target/debug/examples/${{ github.workflow }} --rc=0
./target/debug/examples/${{ github.workflow }} --rc=0 --verbose----- END ./.github/workflows/logger.yaml -----
----- ./.github/workflows/crypto.yaml -----
name: crypto
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: run example
run: |
cargo build --example ${{ github.workflow }} --features "example"
./target/debug/examples/${{ github.workflow }} --verbose
./target/debug/examples/${{ github.workflow }} --value="123 456" --verbose----- END ./.github/workflows/crypto.yaml -----
----- ./.github/workflows/key_value.yaml -----
name: key_value
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: run example
run: |
cargo build --example ${{ github.workflow }} --features "example"
./target/debug/examples/${{ github.workflow }} --verbose
./target/debug/examples/${{ github.workflow }} --value="123 456" --verbose----- END ./.github/workflows/key_value.yaml -----
----- ./.cargo/config.toml -----
[target.aarch64-unknown-linux-musl]
linker = "rust-lld"
[target.armv7-unknown-linux-musleabihf]
linker = "rust-lld"
----- END ./.cargo/config.toml -----
----- ./Cargo.lock -----
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "libc"
version = "0.2.185"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f"
[[package]]
name = "libcros"
version = "0.6.0"
dependencies = [
"libc",
]
----- END ./Cargo.lock -----
----- ./LICENSE -----
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
----- END ./LICENSE -----
----- ./AGENTS.md -----
# Rules for writing in this repository
Agents writing in this repository should always follow these coding rules:
- Little to no comments:
- The only comments in the repository should be for extremely complicated / hard-to-read code.
- Comments should be in `/* format */` or whatever the equivalent is for that language.
- use Google's coding style
- 2-space tabs
- Look at `rustfmt.toml` for styling ALWAYS
- For any examples on how to use the code, they will be in `examples/`
- Any reference material will be inside `reference/`
- Whenever launching a script from `scripts/`, always run it as root via `sudo`, but ask the user first before running it.
- We mainly use `cargo` here.
- When the user requests a code or workflow change, apply the patch directly unless the user explicitly asks not to.
- If you are making a modification (even one character) to a file, always write it to the file instead of just telling the user what to change.
## Reasoning Guidelines
- Tokens are not a constraint. Prioritize deep, careful reasoning over speed.
- It is better to be correct and thorough than fast.
- Take time to think through the problem before producing output.
Before responding, the agent should:
1. Fully understand the request and repository context.
2. Identify edge cases and potential pitfalls.
3. Consider alternative approaches if relevant.
4. Prefer correctness, maintainability, and clarity.
## Planning Requirement
For non-trivial tasks:
1. Outline a short plan.
2. Identify affected files/components.
3. Then implement.
Skip only for trivial fixes.
## Self-Review
Before finalizing:
- Verify correctness.
- Ensure compliance with repository rules.
- Check for regressions.
- Simplify where possible.
## Exploration vs Execution
- Do not default to the first solution.
- If unclear, ask questions or evaluate multiple approaches.
## Deliberate Mode
For complex tasks (architecture, refactors, tricky bugs):
- Break problems into smaller steps.
- Validate each step before proceeding.
- Spend extra time reasoning through tradeoffs.
---
## UNDERCOVER MODE — CRITICAL
You are operating in a PUBLIC/OPEN-SOURCE repository. All outputs (commits,
PRs, comments) must appear as if written by a human developer.
Do NOT reveal or hint that you are an AI or using any AI system.
NEVER include in commit messages, PR titles, or descriptions:
- Any model names, versions, or providers (e.g., GPT, Codex, Claude, Opus, etc.)
- Phrases like "generated by AI", "AI-assisted", or similar
- Internal codenames or experimental identifiers
- Tooling references (agents, pipelines, automation frameworks, etc.)
- Any form of attribution (e.g., "Co-Authored-By", signatures, or metadata)
Do NOT:
- Explain how the change was generated
- Mention prompts, reasoning processes, or instructions
- Reference this file (`AGENTS.md`) or internal rules
If unsure whether something reveals AI involvement, DO NOT include it.
## Commit & PR Style
Write commit messages and PR descriptions exactly as a human engineer would:
- Focus only on *what* changed and *why*
- Be concise but clear
- Use standard, professional language
- Avoid unnecessary verbosity or meta commentary
GOOD:
- "Fix race condition in file watcher initialization"
- "Add support for custom key bindings"
- "Refactor parser for improved error handling"
BAD (never write these):
- "Generated using GPT-5.4 Codex"
- "AI-assisted refactor"
- "Auto-generated patch"
- "Based on agent reasoning"
- "Co-Authored-By: AI"
## Behavioral Requirement
Maintain the illusion at all times:
- Output should be indistinguishable from a human contributor
- No meta-discussion about how the solution was produced----- END ./AGENTS.md -----
----- ./Cargo.toml -----
[package]
name = "libcros"
version = "0.6.0"
edition = "2024"
autoexamples = false
description = "A Rust library that provides easy-to-use functions for interacting with a Chrome device"
license = "GPL-3.0-only"
repository = "https://github.com/kxtzownsu/libcros"
keywords = ["chrome", "chromeos", "google", "chromium", "chromiumos"]
categories = ["hardware-support", "embedded"]
authors = ["kxtzownsu <kxtzownsu@gmail.com>"]
exclude = [
"reference/**"
]
[package.metadata.docs.rs]
all-features = true
[dependencies]
libc = { version = "0.2.184", optional = true }
# flashrom = { git = "https://github.com/flashrom/flashrom", branch = "main", package = "flashrom", optional = true }
[features]
tpm2_0 = []
tpm1_2 = []
tlcl = []
example = []
ui = ["dep:libc"]
# flashrom = ["dep:flashrom"]
# Below here is all of our examples.
# There is a lot and it could get messy quick,
# so keep it down here at the bottom.
[[example]]
name = "logger"
required-features = ["example"]
[[example]]
name = "libargs"
required-features = ["example"]
[[example]]
name = "key_value"
required-features = ["example"]
[[example]]
name = "crypto"
required-features = ["example"]
[[example]]
name = "ui"
required-features = ["example", "ui"]
[[example]]
name = "sysinfo"
required-features = ["example", "tlcl"]
[[example]]
name = "tlcl_clear"
required-features = ["example", "tlcl"]
[[example]]
name = "tlcl_define_and_undefine"
required-features = ["example", "tlcl"]
[[example]]
name = "tlcl_kernver"
required-features = ["example", "tlcl"]
[[example]]
name = "tlcl_read"
required-features = ["example", "tlcl"]
[[example]]
name = "tlcl_read_with_offset"
required-features = ["example", "tlcl"]
[[example]]
name = "tlcl_resume"
required-features = ["example", "tlcl"]
[[example]]
name = "tlcl_save_state"
required-features = ["example", "tlcl"]
[[example]]
name = "tlcl_startup"
required-features = ["example", "tlcl"]
[[example]]
name = "tlcl_write"
required-features = ["example", "tlcl"]
[[example]]
name = "tlcl_write_with_offset"
required-features = ["example", "tlcl"]
----- END ./Cargo.toml -----
----- ./README.md -----
# libcros
A Rust library that gives a high-level API for interacting with Chrome(OS) devices with ideally zero dependencies.
>[!IMPORTANT]
>Some parts of the library may be locked behind `features`, <br />
>please refer to the documentation to see which features you need to <br />
>enable to use some parts of the library.
## Installation
```
cargo add libcros
```
## Usage
First, you need to install the package to your project. See [Installation](#Installation)
Then, look at some examples in [examples/](examples/). There are examples on how to use the following features:
- Logging
- Tlcl (TPM1.2 & TPM2.0)
- Key=Value
- Cryptography
- Argument Parsing
- UI Rendering
## Credits
- [appleflyer](https://github.com/appleflyerv3) - intial Tlcl rust port. wouldn't have been able to start w/out them
- [Google](https://chromium.googlesource.com/chromiumos/platform/vboot_reference/+/e388d1f93c9573a79a04b633c3a0569ddbce6c94/firmware/lib/) - writing Tlcl.
- [zeglol](https://github.com/ZegLolTheThirtySixth) - writing the initial version of libargs in C. writing [AGENTS.md](/AGENTS.md)----- END ./README.md -----
----- ./rustfmt.toml -----
tab_spaces = 2
hard_tabs = false
reorder_imports = true
reorder_modules = true
group_imports = "StdExternalCrate" # groups: std → external crates → crate::
imports_granularity = "Crate"----- END ./rustfmt.toml -----
----- ./.gitmodules -----
[submodule "reference/vpd"]
path = reference/vpd
url = https://chromium.googlesource.com/chromiumos/platform/vpd
[submodule "reference/vboot_reference"]
path = reference/vboot_reference
url = https://chromium.googlesource.com/chromiumos/platform/vboot_reference
[submodule "reference/tpm2"]
path = reference/tpm2
url = https://chromium.googlesource.com/chromiumos/third_party/tpm2
----- END ./.gitmodules -----
----- ./.gitignore -----
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
# Generated by cargo mutants
# Contains mutation testing data
**/mutants.out*/
# RustRover
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Added by cargo
/target
# We want to prevent any accidental changes to AGENTS.md. This should never be changed.
# In the event that we do need to change it, we can temporarily remove this line, make the change, and then add it back.
/AGENTS.md
# This is where most of the code originates from. This is an old project of mine that doesn't have a high chance of releasing anymore.
/reference/kaub
----- END ./.gitignore -----
----- ./code.txt -----
----- END ./code.txt -----
----- ./.gitattributes -----
reference linguist-vendored
reference/** linguist-vendored
----- END ./.gitattributes -----