| // Copyright 2022 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use crate::context::Context; |
| use crate::error::HwsecError; |
| use crate::tpm2::gen_tpm_cmd; |
| use crate::tpm2::run_tpm_cmd; |
| use crate::tpm2::CommandArg; |
| use crate::tpm2::SessionOption; |
| use crate::tpm2::TpmiStCommandTag; |
| |
| pub fn nv_read(ctx: &mut impl Context, index: u32, length: u16) -> Result<Vec<u8>, HwsecError> { |
| const SESSION_DESCRIPTION_LENGTH: usize = 6; |
| let tpm_cmd = gen_tpm_cmd( |
| TpmiStCommandTag::TPM_ST_SESSIONS(SessionOption::EmptyPassword), |
| CommandArg::TPM_CC_NV_Read(length), |
| index, |
| )?; |
| let response = run_tpm_cmd(ctx, tpm_cmd)?; |
| if response.success() { |
| if response.body().len() < SESSION_DESCRIPTION_LENGTH + length as usize { |
| // This only happens when TPM is not functioning correctly |
| // by returning less bytes than we requested. |
| Err(HwsecError::InternalError) |
| } else { |
| Ok(response.body() |
| [SESSION_DESCRIPTION_LENGTH..(SESSION_DESCRIPTION_LENGTH + length as usize)] |
| .to_vec()) |
| } |
| } else { |
| Err(HwsecError::Tpm2Error(response.return_code())) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use crate::context::mock::MockContext; |
| use crate::context::Context; |
| use crate::error::HwsecError; |
| use crate::tpm2::nv_read; |
| use crate::tpm2::tests::split_into_hex_strtok; |
| use crate::tpm2::BOARD_ID_INDEX; |
| use crate::tpm2::BOARD_ID_LENGTH; |
| |
| #[test] |
| fn test_nv_read_successful() { |
| let index = BOARD_ID_INDEX; |
| let length = BOARD_ID_LENGTH; |
| |
| let mut mock_ctx = MockContext::new(); |
| |
| mock_ctx.cmd_runner().set_trunksd_running(true); |
| mock_ctx.cmd_runner().add_tpm_interaction( |
| "trunks_send", |
| vec!["--raw"], |
| split_into_hex_strtok( |
| "80 02 00 00 00 23 00 00 \ |
| 01 4e 01 3f ff 00 01 3f \ |
| ff 00 00 00 00 09 40 00 \ |
| 00 09 00 00 00 00 00 00 \ |
| 0c 00 00", |
| ), |
| 0, |
| "800200000021000000000000000E000C4646524DB9B9ADB27F7F00000000010000", |
| "", |
| ); |
| |
| let result = nv_read(&mut mock_ctx, index, length); |
| |
| assert_eq!( |
| result, |
| Ok(vec![ |
| 0x46, 0x46, 0x52, 0x4d, 0xb9, 0xb9, 0xad, 0xb2, 0x7f, 0x7f, 0x00, 0x00 |
| ]) |
| ); |
| } |
| #[test] |
| fn test_nv_read_length_too_large() { |
| let index = BOARD_ID_INDEX; |
| let length = 13_u16; |
| |
| let mut mock_ctx = MockContext::new(); |
| |
| mock_ctx.cmd_runner().set_trunksd_running(true); |
| mock_ctx.cmd_runner().add_tpm_interaction( |
| "trunks_send", |
| vec!["--raw"], |
| split_into_hex_strtok( |
| "80 02 00 00 00 23 00 00 \ |
| 01 4e 01 3f ff 00 01 3f \ |
| ff 00 00 00 00 09 40 00 \ |
| 00 09 00 00 00 00 00 00 \ |
| 0d 00 00", |
| ), |
| 0, |
| "80010000000A00000146", |
| "", |
| ); |
| |
| let result = nv_read(&mut mock_ctx, index, length); |
| |
| // return code 0x00000146 = TPM_RC_NV_RANGE = RC_VER1(0x100) + 0x046 |
| assert_eq!(result, Err(HwsecError::Tpm2Error(0x00000146))); |
| } |
| #[test] |
| fn test_nv_read_nonzero_exit_status() { |
| let index = BOARD_ID_INDEX; |
| let length = 13_u16; |
| |
| let mut mock_ctx = MockContext::new(); |
| |
| mock_ctx.cmd_runner().set_trunksd_running(true); |
| mock_ctx.cmd_runner().add_tpm_interaction( |
| "trunks_send", |
| vec!["--raw"], |
| split_into_hex_strtok( |
| "80 02 00 00 00 23 00 00 \ |
| 01 4e 01 3f ff 00 01 3f \ |
| ff 00 00 00 00 09 40 00 \ |
| 00 09 00 00 00 00 00 00 \ |
| 0d 00 00", |
| ), |
| 1, |
| "", |
| "", |
| ); |
| |
| let result = nv_read(&mut mock_ctx, index, length); |
| assert_eq!(result, Err(HwsecError::CommandRunnerError)); |
| } |
| #[test] |
| fn test_tpmc_raw_successful() { |
| let index = BOARD_ID_INDEX; |
| let length = 12_u16; |
| |
| let mut mock_ctx = MockContext::new(); |
| |
| mock_ctx.cmd_runner().set_trunksd_running(false); |
| mock_ctx.cmd_runner().add_tpm_interaction( |
| "tpmc", |
| vec!["raw"], |
| split_into_hex_strtok( |
| "80 02 00 00 00 23 00 00 \ |
| 01 4e 01 3f ff 00 01 3f \ |
| ff 00 00 00 00 09 40 00 \ |
| 00 09 00 00 00 00 00 00 \ |
| 0c 00 00", |
| ), |
| 0, |
| "0x80 0x02 0x00 0x00 0x00 0x21 0x00 0x00 \n |
| 0x00 0x00 0x00 0x00 0x00 0x0e 0x00 0x0c \n |
| 0x46 0x46 0x52 0x4d 0xb9 0xb9 0xad 0xb2 \n |
| 0x7f 0x7f 0x00 0x00 0x00 0x00 0x01 0x00 \n |
| 0x00", |
| "", |
| ); |
| |
| let result = nv_read(&mut mock_ctx, index, length); |
| assert_eq!( |
| result, |
| Ok(vec![ |
| 0x46, 0x46, 0x52, 0x4d, 0xb9, 0xb9, 0xad, 0xb2, 0x7f, 0x7f, 0x00, 0x00 |
| ]) |
| ); |
| } |
| #[test] |
| fn nonzero_tpmc_return_code() { |
| let index = BOARD_ID_INDEX; |
| let length = 12_u16; |
| |
| let mut mock_ctx = MockContext::new(); |
| |
| mock_ctx.cmd_runner().set_trunksd_running(false); |
| mock_ctx.cmd_runner().add_tpm_interaction( |
| "tpmc", |
| vec!["raw"], |
| split_into_hex_strtok( |
| "80 02 00 00 00 23 00 00 \ |
| 01 4e 01 3f ff 00 01 3f \ |
| ff 00 00 00 00 09 40 00 \ |
| 00 09 00 00 00 00 00 00 \ |
| 0c 00 00", |
| ), |
| -1, |
| "", |
| "bad byte value \"8002000000230000014e013\ |
| fff00013fff0000000009400000090000000000000c0000\"", |
| ); |
| |
| let result = nv_read(&mut mock_ctx, index, length); |
| assert_eq!(result, Err(HwsecError::CommandRunnerError)); |
| } |
| } |