// // Copyright (C) 2014 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Note: These tests are not generated. They test generated code. #include <base/bind.h> #include <base/callback.h> #include <base/message_loop/message_loop.h> #include <base/run_loop.h> #include <gtest/gtest.h> #include "trunks/mock_authorization_delegate.h" #include "trunks/mock_command_transceiver.h" #include "trunks/tpm_generated.h" using testing::_; using testing::DoAll; using testing::Invoke; using testing::Return; using testing::SetArgPointee; using testing::StrictMock; using testing::WithArg; namespace trunks { // This test is designed to get good coverage of the different types of code // generated for serializing and parsing structures / unions / typedefs. TEST(GeneratorTest, SerializeParseStruct) { TPM2B_CREATION_DATA data; memset(&data, 0, sizeof(TPM2B_CREATION_DATA)); data.creation_data.pcr_select.count = 1; data.creation_data.pcr_select.pcr_selections[0].hash = TPM_ALG_SHA256; data.creation_data.pcr_select.pcr_selections[0].sizeof_select = 1; data.creation_data.pcr_select.pcr_selections[0].pcr_select[0] = 0; data.creation_data.pcr_digest.size = 2; data.creation_data.locality = 0; data.creation_data.parent_name_alg = TPM_ALG_SHA256; data.creation_data.parent_name.size = 3; data.creation_data.parent_qualified_name.size = 4; data.creation_data.outside_info.size = 5; std::string buffer; TPM_RC rc = Serialize_TPM2B_CREATION_DATA(data, &buffer); ASSERT_EQ(TPM_RC_SUCCESS, rc); EXPECT_EQ(35u, buffer.size()); TPM2B_CREATION_DATA data2; memset(&data2, 0, sizeof(TPM2B_CREATION_DATA)); std::string buffer_before = buffer; std::string buffer_parsed; rc = Parse_TPM2B_CREATION_DATA(&buffer, &data2, &buffer_parsed); ASSERT_EQ(TPM_RC_SUCCESS, rc); EXPECT_EQ(0u, buffer.size()); EXPECT_EQ(buffer_before, buffer_parsed); EXPECT_EQ(buffer_before.size() - 2, data2.size); EXPECT_EQ(0, memcmp(&data.creation_data, &data2.creation_data, sizeof(TPMS_CREATION_DATA))); } TEST(GeneratorTest, SerializeBufferOverflow) { TPM2B_MAX_BUFFER value; value.size = arraysize(value.buffer) + 1; std::string tmp; EXPECT_EQ(TPM_RC_INSUFFICIENT, Serialize_TPM2B_MAX_BUFFER(value, &tmp)); } TEST(GeneratorTest, ParseBufferOverflow) { TPM2B_MAX_BUFFER tmp; // Case 1: Sufficient source but overflow the destination. std::string malformed1 = "\x10\x00"; malformed1 += std::string(0x1000, 'A'); ASSERT_GT(0x1000u, sizeof(tmp.buffer)); EXPECT_EQ(TPM_RC_INSUFFICIENT, Parse_TPM2B_MAX_BUFFER(&malformed1, &tmp, nullptr)); // Case 2: Sufficient destination but overflow the source. std::string malformed2 = "\x00\x01"; EXPECT_EQ(TPM_RC_INSUFFICIENT, Parse_TPM2B_MAX_BUFFER(&malformed2, &tmp, nullptr)); } TEST(GeneratorTest, SynchronousCommand) { // A hand-rolled TPM2_Startup command. std::string expected_command("\x80\x01" // tag=TPM_ST_NO_SESSIONS "\x00\x00\x00\x0C" // size=12 "\x00\x00\x01\x44" // code=TPM_CC_Startup "\x00\x00", // param=TPM_SU_CLEAR 12); std::string command_response("\x80\x01" // tag=TPM_ST_NO_SESSIONS "\x00\x00\x00\x0A" // size=10 "\x00\x00\x00\x00", // code=TPM_RC_SUCCESS 10); StrictMock<MockCommandTransceiver> transceiver; EXPECT_CALL(transceiver, SendCommandAndWait(expected_command)) .WillOnce(Return(command_response)); StrictMock<MockAuthorizationDelegate> authorization; EXPECT_CALL(authorization, GetCommandAuthorization(_, _, _, _)) .WillOnce(Return(true)); Tpm tpm(&transceiver); EXPECT_EQ(TPM_RC_SUCCESS, tpm.StartupSync(TPM_SU_CLEAR, &authorization)); } TEST(GeneratorTest, SynchronousCommandWithError) { // A hand-rolled TPM2_Startup command. std::string expected_command("\x80\x01" // tag=TPM_ST_NO_SESSIONS "\x00\x00\x00\x0C" // size=12 "\x00\x00\x01\x44" // code=TPM_CC_Startup "\x00\x00", // param=TPM_SU_CLEAR 12); std::string command_response("\x80\x01" // tag=TPM_ST_NO_SESSIONS "\x00\x00\x00\x0A" // size=10 "\x00\x00\x01\x01", // code=TPM_RC_FAILURE 10); StrictMock<MockCommandTransceiver> transceiver; EXPECT_CALL(transceiver, SendCommandAndWait(expected_command)) .WillOnce(Return(command_response)); StrictMock<MockAuthorizationDelegate> authorization; EXPECT_CALL(authorization, GetCommandAuthorization(_, _, _, _)) .WillOnce(Return(true)); Tpm tpm(&transceiver); EXPECT_EQ(TPM_RC_FAILURE, tpm.StartupSync(TPM_SU_CLEAR, &authorization)); } TEST(GeneratorTest, SynchronousCommandResponseTest) { std::string auth_in(10, 'A'); std::string auth_out(10, 'B'); std::string auth_size("\x00\x00\x00\x0A", 4); std::string handle_in("\x40\x00\x00\x07", 4); // primary_handle = TPM_RH_NULL std::string handle_out("\x80\x00\x00\x01", 4); // out_handle std::string sensitive("\x00\x05" // sensitive.size = 5 "\x00\x01" // sensitive.auth.size = 1 "\x61" // sensitive.auth.buffer[0] = 0x65 "\x00\x00", // sensitive.data.size = 0 7); std::string public_data("\x00\x12" // public.size = 18 "\x00\x25" // public.type = TPM_ALG_SYMCIPHER "\x00\x0B" // public.name_alg = SHA256 "\x00\x00\x00\x00" "\x00\x00" // public.auth_policy.size = 0 "\x00\x06" // public.sym.alg = TPM_ALG_AES "\x00\x80" // public.sym.key_bits = 128 "\x00\x43" // public.sym.mode = TPM_ALG_CFB "\x00\x00", // public.unique.size = 0 20); std::string outside("\x00\x00", 2); // outside_info.size = 0 std::string pcr_select("\x00\x00\x00\x00", 4); // pcr_select.size = 0 std::string data("\x00\x0F" // creation_data.size = 15 "\x00\x00\x00\x00" // creation.pcr = 0 "\x00\x00" // creation.digest.size = 0 "\x00" // creation.locality = 0 "\x00\x00" // creation.parent_alg = 0 "\x00\x00" // creation.parent_name.size = 0 "\x00\x00" "\x00\x00", // creation.outside.size = 0 17); std::string hash("\x00\x01" "\x62", 3); std::string ticket("\x80\x02" // tag = TPM_ST_SESSIONS "\x40\x00\x00\x07" // parent = TPM_RH_NULL "\x00\x00", 8); std::string name("\x00\x03" "KEY", 5); std::string parameter_size("\x00\x00\x00\x35", 4); // param_size = 38 std::string command_tag("\x80\x02" // tag = TPM_ST_SESSIONS "\x00\x00\x00\x3D" // size = 61 "\x00\x00\x01\x31", // code = TPM_CC_CreatePrimary 10); std::string response_tag("\x80\x02" // tag = TPM_ST_SESSIONS "\x00\x00\x00\x51" // size = 79 "\x00\x00\x00\x00", // rc = TPM_RC_SUCCESS 10); std::string expected_command = command_tag + handle_in + auth_size + auth_in + sensitive + public_data + outside + pcr_select; std::string command_response = response_tag + handle_out + parameter_size + public_data + data + hash + ticket + name + auth_out; StrictMock<MockCommandTransceiver> transceiver; EXPECT_CALL(transceiver, SendCommandAndWait(expected_command)) .WillOnce(Return(command_response)); StrictMock<MockAuthorizationDelegate> authorization; EXPECT_CALL(authorization, GetCommandAuthorization(_, _, _, _)) .WillOnce(DoAll(SetArgPointee<3>(auth_in), Return(true))); EXPECT_CALL(authorization, CheckResponseAuthorization(_, auth_out)) .WillOnce(Return(true)); EXPECT_CALL(authorization, EncryptCommandParameter(_)) .WillOnce(Return(true)); EXPECT_CALL(authorization, DecryptResponseParameter(_)) .WillOnce(Return(true)); TPM2B_SENSITIVE_CREATE in_sensitive; in_sensitive.size = 5; in_sensitive.sensitive.user_auth.size = 1; in_sensitive.sensitive.user_auth.buffer[0] = 'a'; in_sensitive.sensitive.data.size = 0; TPM2B_PUBLIC in_public; in_public.size = 18; in_public.public_area.type = TPM_ALG_SYMCIPHER; in_public.public_area.name_alg = TPM_ALG_SHA256; in_public.public_area.object_attributes = 0; in_public.public_area.auth_policy.size = 0; in_public.public_area.parameters.sym_detail.sym.algorithm = TPM_ALG_AES; in_public.public_area.parameters.sym_detail.sym.key_bits.aes = 128; in_public.public_area.parameters.sym_detail.sym.mode.aes = TPM_ALG_CFB; in_public.public_area.unique.sym.size = 0; TPM2B_DATA outside_info; outside_info.size = 0; TPML_PCR_SELECTION create_pcr; create_pcr.count = 0; TPM_HANDLE key_handle; TPM2B_PUBLIC out_public; TPM2B_CREATION_DATA creation_data; TPM2B_DIGEST creation_hash; TPMT_TK_CREATION creation_ticket; TPM2B_NAME key_name; Tpm tpm(&transceiver); TPM_RC rc = tpm.CreatePrimarySync(trunks::TPM_RH_NULL, "", in_sensitive, in_public, outside_info, create_pcr, &key_handle, &out_public, &creation_data, &creation_hash, &creation_ticket, &key_name, &authorization); ASSERT_EQ(rc, TPM_RC_SUCCESS); EXPECT_EQ(key_handle, 0x80000001); EXPECT_EQ(out_public.size, 18); EXPECT_EQ(creation_data.size, 15); EXPECT_EQ(creation_hash.size, 1); EXPECT_EQ(creation_hash.buffer[0], 'b'); EXPECT_EQ(creation_ticket.tag, 0x8002); EXPECT_EQ(creation_ticket.hierarchy, 0x40000007u); EXPECT_EQ(creation_ticket.digest.size, 0); EXPECT_EQ(key_name.size, 3); EXPECT_EQ(key_name.name[0], 'K'); EXPECT_EQ(key_name.name[1], 'E'); EXPECT_EQ(key_name.name[2], 'Y'); } // A fixture for asynchronous command flow tests. class CommandFlowTest : public testing::Test { public: CommandFlowTest() : response_code_(TPM_RC_SUCCESS) {} ~CommandFlowTest() override {} void StartupCallback(TPM_RC response_code) { response_code_ = response_code; } void CertifyCallback(TPM_RC response_code, const TPM2B_ATTEST& certify_info, const TPMT_SIGNATURE& signature) { response_code_ = response_code; signed_data_ = StringFrom_TPM2B_ATTEST(certify_info); signature_ = StringFrom_TPM2B_PUBLIC_KEY_RSA( signature.signature.rsassa.sig); } protected: void Run() { base::RunLoop run_loop; run_loop.RunUntilIdle(); } base::MessageLoop message_loop_; TPM_RC response_code_; std::string signature_; std::string signed_data_; }; // A functor for posting command responses. This is different than invoking the // callback directly (e.g. via InvokeArgument) in that the original call will // return before the response callback is invoked. This more closely matches how // this code is expected to work when integrated. class PostResponse { public: explicit PostResponse(const std::string& response) : response_(response) {} void operator() (const base::Callback<void(const std::string&)>& callback) { base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(callback, response_)); } private: std::string response_; }; // A functor to handle fake encryption / decryption of parameters. class Encryptor { public: Encryptor(const std::string& expected_input, const std::string& output) : expected_input_(expected_input), output_(output) {} bool operator() (std::string* value) { EXPECT_EQ(expected_input_, *value); value->assign(output_); return true; } private: std::string expected_input_; std::string output_; }; TEST_F(CommandFlowTest, SimpleCommandFlow) { // A hand-rolled TPM2_Startup command. std::string expected_command("\x80\x01" // tag=TPM_ST_NO_SESSIONS "\x00\x00\x00\x0C" // size=12 "\x00\x00\x01\x44" // code=TPM_CC_Startup "\x00\x00", // param=TPM_SU_CLEAR 12); std::string command_response("\x80\x01" // tag=TPM_ST_NO_SESSIONS "\x00\x00\x00\x0A" // size=10 "\x00\x00\x00\x00", // code=TPM_RC_SUCCESS 10); StrictMock<MockCommandTransceiver> transceiver; EXPECT_CALL(transceiver, SendCommand(expected_command, _)) .WillOnce(WithArg<1>(Invoke(PostResponse(command_response)))); StrictMock<MockAuthorizationDelegate> authorization; EXPECT_CALL(authorization, GetCommandAuthorization(_, _, _, _)) .WillOnce(Return(true)); Tpm tpm(&transceiver); response_code_ = TPM_RC_FAILURE; tpm.Startup(TPM_SU_CLEAR, &authorization, base::Bind(&CommandFlowTest::StartupCallback, base::Unretained(this))); Run(); EXPECT_EQ(TPM_RC_SUCCESS, response_code_); } TEST_F(CommandFlowTest, SimpleCommandFlowWithError) { // A hand-rolled TPM2_Startup command. std::string expected_command("\x80\x01" // tag=TPM_ST_NO_SESSIONS "\x00\x00\x00\x0C" // size=12 "\x00\x00\x01\x44" // code=TPM_CC_Startup "\x00\x00", // param=TPM_SU_CLEAR 12); std::string command_response("\x80\x01" // tag=TPM_ST_NO_SESSIONS "\x00\x00\x00\x0A" // size=10 "\x00\x00\x01\x01", // code=TPM_RC_FAILURE 10); StrictMock<MockCommandTransceiver> transceiver; EXPECT_CALL(transceiver, SendCommand(expected_command, _)) .WillOnce(WithArg<1>(Invoke(PostResponse(command_response)))); StrictMock<MockAuthorizationDelegate> authorization; EXPECT_CALL(authorization, GetCommandAuthorization(_, _, _, _)) .WillOnce(Return(true)); Tpm tpm(&transceiver); tpm.Startup(TPM_SU_CLEAR, &authorization, base::Bind(&CommandFlowTest::StartupCallback, base::Unretained(this))); Run(); EXPECT_EQ(TPM_RC_FAILURE, response_code_); } // This test is designed to get good coverage of the different types of code // generated for command / response processing. It covers: // - input handles // - authorization // - multiple input and output parameters // - parameter encryption and decryption TEST_F(CommandFlowTest, FullCommandFlow) { // A hand-rolled TPM2_Certify command. std::string auth_in(10, 'A'); std::string auth_out(20, 'B'); std::string user_data("\x00\x0C" "ct_user_data", 14); std::string scheme("\x00\x10", 2); // scheme=TPM_ALG_NULL std::string signed_data("\x00\x0E" "ct_signed_data", 16); std::string signature("\x00\x14" // sig_scheme=RSASSA "\x00\x0B" // hash_scheme=SHA256 "\x00\x09" // signature size "signature", // signature bytes 15); std::string expected_command("\x80\x02" // tag=TPM_ST_SESSIONS "\x00\x00\x00\x30" // size=48 "\x00\x00\x01\x48" // code=TPM_CC_Certify "\x11\x22\x33\x44" // @objectHandle "\x55\x66\x77\x88" // @signHandle "\x00\x00\x00\x0A", // auth_size=10 22); expected_command += auth_in + user_data + scheme; std::string command_response("\x80\x02" // tag=TPM_ST_SESSIONS "\x00\x00\x00\x41" // size=65 "\x00\x00\x00\x00" // code=TPM_RC_SUCCESS "\x00\x00\x00\x1F", // param_size=31 14); command_response += signed_data + signature + auth_out; StrictMock<MockCommandTransceiver> transceiver; EXPECT_CALL(transceiver, SendCommand(expected_command, _)) .WillOnce(WithArg<1>(Invoke(PostResponse(command_response)))); StrictMock<MockAuthorizationDelegate> authorization; EXPECT_CALL(authorization, GetCommandAuthorization(_, _, _, _)) .WillOnce(DoAll(SetArgPointee<3>(auth_in), Return(true))); EXPECT_CALL(authorization, CheckResponseAuthorization(_, auth_out)) .WillOnce(Return(true)); EXPECT_CALL(authorization, EncryptCommandParameter(_)) .WillOnce(Invoke(Encryptor("pt_user_data", "ct_user_data"))); EXPECT_CALL(authorization, DecryptResponseParameter(_)) .WillOnce(Invoke(Encryptor("ct_signed_data", "pt_signed_data"))); TPMT_SIG_SCHEME null_scheme; null_scheme.scheme = TPM_ALG_NULL; null_scheme.details.rsassa.hash_alg = TPM_ALG_SHA256; Tpm tpm(&transceiver); tpm.Certify(0x11223344u, "object_handle", 0x55667788u, "sign_handle", Make_TPM2B_DATA("pt_user_data"), null_scheme, &authorization, base::Bind(&CommandFlowTest::CertifyCallback, base::Unretained(this))); Run(); ASSERT_EQ(TPM_RC_SUCCESS, response_code_); EXPECT_EQ("pt_signed_data", signed_data_); EXPECT_EQ("signature", signature_); } } // namespace trunks