| // |
| // Copyright (C) 2015 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. |
| // |
| |
| #include "trunks/resource_manager.h" |
| |
| #include <string> |
| #include <vector> |
| |
| #include <base/bind.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| #include "trunks/error_codes.h" |
| #include "trunks/mock_command_transceiver.h" |
| #include "trunks/mock_tpm.h" |
| #include "trunks/trunks_factory_for_test.h" |
| |
| using testing::_; |
| using testing::DoAll; |
| using testing::Eq; |
| using testing::Field; |
| using testing::InSequence; |
| using testing::Return; |
| using testing::ReturnPointee; |
| using testing::SetArgumentPointee; |
| using testing::StrictMock; |
| |
| namespace { |
| |
| const trunks::TPM_HANDLE kArbitraryObjectHandle = trunks::TRANSIENT_FIRST + 25; |
| const trunks::TPM_HANDLE kArbitrarySessionHandle = trunks::HMAC_SESSION_FIRST; |
| |
| void Assign(std::string* to, const std::string& from) { |
| *to = from; |
| } |
| |
| class ScopedDisableLogging { |
| public: |
| ScopedDisableLogging() : original_severity_(logging::GetMinLogLevel()) { |
| logging::SetMinLogLevel(logging::LOG_FATAL); |
| } |
| ~ScopedDisableLogging() { logging::SetMinLogLevel(original_severity_); } |
| |
| private: |
| logging::LogSeverity original_severity_; |
| }; |
| |
| } // namespace |
| |
| namespace trunks { |
| |
| class ResourceManagerTest : public testing::Test { |
| public: |
| const std::vector<TPM_HANDLE> kNoHandles; |
| const std::string kNoAuthorization; |
| const std::string kNoParameters; |
| |
| ResourceManagerTest() : resource_manager_(factory_, &transceiver_) {} |
| ~ResourceManagerTest() override {} |
| |
| void SetUp() override { factory_.set_tpm(&tpm_); } |
| |
| // Builds a well-formed command. |
| std::string CreateCommand(TPM_CC code, |
| const std::vector<TPM_HANDLE>& handles, |
| const std::string& authorization, |
| const std::string& parameters) { |
| std::string buffer; |
| TPM_ST tag = authorization.empty() ? TPM_ST_NO_SESSIONS : TPM_ST_SESSIONS; |
| UINT32 size = 10 + (handles.size() * 4) + authorization.size() + |
| parameters.size() + (authorization.empty() ? 0 : 4); |
| Serialize_TPM_ST(tag, &buffer); |
| Serialize_UINT32(size, &buffer); |
| Serialize_TPM_CC(code, &buffer); |
| for (auto handle : handles) { |
| Serialize_TPM_HANDLE(handle, &buffer); |
| } |
| if (!authorization.empty()) { |
| Serialize_UINT32(authorization.size(), &buffer); |
| } |
| return buffer + authorization + parameters; |
| } |
| |
| // Builds a well-formed response. |
| std::string CreateResponse(TPM_RC code, |
| const std::vector<TPM_HANDLE>& handles, |
| const std::string& authorization, |
| const std::string& parameters) { |
| std::string buffer; |
| TPM_ST tag = authorization.empty() ? TPM_ST_NO_SESSIONS : TPM_ST_SESSIONS; |
| UINT32 size = 10 + (handles.size() * 4) + authorization.size() + |
| parameters.size() + (authorization.empty() ? 0 : 4); |
| Serialize_TPM_ST(tag, &buffer); |
| Serialize_UINT32(size, &buffer); |
| Serialize_TPM_RC(code, &buffer); |
| for (auto handle : handles) { |
| Serialize_TPM_HANDLE(handle, &buffer); |
| } |
| if (!authorization.empty()) { |
| Serialize_UINT32(parameters.size(), &buffer); |
| } |
| return buffer + parameters + authorization; |
| } |
| |
| // Builds a well-formed command authorization section. |
| std::string CreateCommandAuthorization(TPM_HANDLE handle, |
| bool continue_session) { |
| std::string buffer; |
| Serialize_TPM_HANDLE(handle, &buffer); |
| Serialize_TPM2B_NONCE(Make_TPM2B_DIGEST(std::string(32, 'A')), &buffer); |
| Serialize_BYTE(continue_session ? 1 : 0, &buffer); |
| Serialize_TPM2B_DIGEST(Make_TPM2B_DIGEST(std::string(32, 'B')), &buffer); |
| return buffer; |
| } |
| |
| // Builds a well-formed response authorization section. |
| std::string CreateResponseAuthorization(bool continue_session) { |
| std::string buffer; |
| Serialize_TPM2B_NONCE(Make_TPM2B_DIGEST(std::string(32, 'A')), &buffer); |
| Serialize_BYTE(continue_session ? 1 : 0, &buffer); |
| Serialize_TPM2B_DIGEST(Make_TPM2B_DIGEST(std::string(32, 'B')), &buffer); |
| return buffer; |
| } |
| |
| std::string GetHeader(const std::string& message) { |
| return message.substr(0, 10); |
| } |
| |
| std::string StripHeader(const std::string& message) { |
| return message.substr(10); |
| } |
| |
| // Makes the resource manager aware of a transient object handle and returns |
| // the newly associated virtual handle. |
| TPM_HANDLE LoadHandle(TPM_HANDLE handle) { |
| std::vector<TPM_HANDLE> input_handles = {PERSISTENT_FIRST}; |
| std::string command = CreateCommand(TPM_CC_Load, input_handles, |
| kNoAuthorization, kNoParameters); |
| std::vector<TPM_HANDLE> output_handles = {handle}; |
| std::string response = CreateResponse(TPM_RC_SUCCESS, output_handles, |
| kNoAuthorization, kNoParameters); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(command)) |
| .WillOnce(Return(response)); |
| std::string actual_response = resource_manager_.SendCommandAndWait(command); |
| std::string handle_blob = StripHeader(actual_response); |
| TPM_HANDLE virtual_handle; |
| CHECK_EQ(TPM_RC_SUCCESS, |
| Parse_TPM_HANDLE(&handle_blob, &virtual_handle, NULL)); |
| return virtual_handle; |
| } |
| |
| // Causes the resource manager to evict existing object handles. |
| void EvictObjects() { |
| std::string command = CreateCommand(TPM_CC_Startup, kNoHandles, |
| kNoAuthorization, kNoParameters); |
| std::string response = CreateErrorResponse(TPM_RC_OBJECT_MEMORY); |
| std::string success_response = CreateResponse( |
| TPM_RC_SUCCESS, kNoHandles, kNoAuthorization, kNoParameters); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(_)) |
| .WillOnce(Return(response)) |
| .WillRepeatedly(Return(success_response)); |
| EXPECT_CALL(tpm_, ContextSaveSync(_, _, _, _)) |
| .WillRepeatedly(Return(TPM_RC_SUCCESS)); |
| EXPECT_CALL(tpm_, FlushContextSync(_, _)) |
| .WillRepeatedly(Return(TPM_RC_SUCCESS)); |
| resource_manager_.SendCommandAndWait(command); |
| } |
| |
| // Makes the resource manager aware of a session handle. |
| void StartSession(TPM_HANDLE handle) { |
| std::vector<TPM_HANDLE> input_handles = {1, 2}; |
| std::string command = CreateCommand(TPM_CC_StartAuthSession, input_handles, |
| kNoAuthorization, kNoParameters); |
| std::vector<TPM_HANDLE> output_handles = {handle}; |
| std::string response = CreateResponse(TPM_RC_SUCCESS, output_handles, |
| kNoAuthorization, kNoParameters); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(command)) |
| .WillOnce(Return(response)); |
| std::string actual_response = resource_manager_.SendCommandAndWait(command); |
| ASSERT_EQ(response, actual_response); |
| } |
| |
| // Causes the resource manager to evict an existing session handle. |
| void EvictSession() { |
| std::string command = CreateCommand(TPM_CC_Startup, kNoHandles, |
| kNoAuthorization, kNoParameters); |
| std::string response = CreateErrorResponse(TPM_RC_SESSION_MEMORY); |
| std::string success_response = CreateResponse( |
| TPM_RC_SUCCESS, kNoHandles, kNoAuthorization, kNoParameters); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(_)) |
| .WillOnce(Return(response)) |
| .WillRepeatedly(Return(success_response)); |
| EXPECT_CALL(tpm_, ContextSaveSync(_, _, _, _)) |
| .WillOnce(Return(TPM_RC_SUCCESS)); |
| resource_manager_.SendCommandAndWait(command); |
| } |
| |
| // Creates a TPMS_CONTEXT with the given sequence field. |
| TPMS_CONTEXT CreateContext(UINT64 sequence) { |
| TPMS_CONTEXT context; |
| memset(&context, 0, sizeof(context)); |
| context.sequence = sequence; |
| return context; |
| } |
| |
| // Creates a serialized TPMS_CONTEXT with the given sequence field. |
| std::string CreateContextParameter(UINT64 sequence) { |
| std::string buffer; |
| Serialize_TPMS_CONTEXT(CreateContext(sequence), &buffer); |
| return buffer; |
| } |
| |
| protected: |
| StrictMock<MockTpm> tpm_; |
| TrunksFactoryForTest factory_; |
| StrictMock<MockCommandTransceiver> transceiver_; |
| ResourceManager resource_manager_; |
| }; |
| |
| TEST_F(ResourceManagerTest, BasicPassThrough) { |
| std::string command = CreateCommand(TPM_CC_Startup, kNoHandles, |
| kNoAuthorization, kNoParameters); |
| std::string response = CreateResponse(TPM_RC_SUCCESS, kNoHandles, |
| kNoAuthorization, kNoParameters); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(command)) |
| .WillOnce(Return(response)); |
| std::string actual_response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(actual_response, response); |
| } |
| |
| TEST_F(ResourceManagerTest, BasicPassThroughAsync) { |
| std::string command = CreateCommand(TPM_CC_Startup, kNoHandles, |
| kNoAuthorization, kNoParameters); |
| std::string response = CreateResponse(TPM_RC_SUCCESS, kNoHandles, |
| kNoAuthorization, kNoParameters); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(command)) |
| .WillOnce(Return(response)); |
| std::string actual_response; |
| CommandTransceiver::ResponseCallback callback = |
| base::Bind(&Assign, &actual_response); |
| resource_manager_.SendCommand(command, callback); |
| EXPECT_EQ(actual_response, response); |
| } |
| |
| TEST_F(ResourceManagerTest, VirtualHandleOutput) { |
| std::vector<TPM_HANDLE> input_handles = {PERSISTENT_FIRST}; |
| std::string command = CreateCommand(TPM_CC_Load, input_handles, |
| kNoAuthorization, kNoParameters); |
| std::vector<TPM_HANDLE> output_handles = {kArbitraryObjectHandle}; |
| std::string response = CreateResponse(TPM_RC_SUCCESS, output_handles, |
| kNoAuthorization, kNoParameters); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(command)) |
| .WillOnce(Return(response)); |
| std::string actual_response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(response.size(), actual_response.size()); |
| // We expect the resource manager has replaced the output handle with a |
| // virtual handle (which we can't predict, but it's unlikely to be the same as |
| // the handle emitted by the mock). |
| EXPECT_EQ(GetHeader(response), GetHeader(actual_response)); |
| EXPECT_NE(StripHeader(response), StripHeader(actual_response)); |
| TPM_HT handle_type = static_cast<TPM_HT>(StripHeader(actual_response)[0]); |
| EXPECT_EQ(TPM_HT_TRANSIENT, handle_type); |
| } |
| |
| TEST_F(ResourceManagerTest, VirtualHandleInput) { |
| TPM_HANDLE tpm_handle = kArbitraryObjectHandle; |
| TPM_HANDLE virtual_handle = LoadHandle(tpm_handle); |
| std::vector<TPM_HANDLE> input_handles = {virtual_handle}; |
| std::string command = CreateCommand(TPM_CC_Sign, input_handles, |
| kNoAuthorization, kNoParameters); |
| // We expect the resource manager to replace |virtual_handle| with |
| // |tpm_handle|. |
| std::vector<TPM_HANDLE> expected_input_handles = {tpm_handle}; |
| std::string expected_command = CreateCommand( |
| TPM_CC_Sign, expected_input_handles, kNoAuthorization, kNoParameters); |
| std::string response = CreateResponse(TPM_RC_SUCCESS, kNoHandles, |
| kNoAuthorization, kNoParameters); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(expected_command)) |
| .WillOnce(Return(response)); |
| std::string actual_response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(response, actual_response); |
| } |
| |
| TEST_F(ResourceManagerTest, VirtualHandleCleanup) { |
| TPM_HANDLE tpm_handle = kArbitraryObjectHandle; |
| TPM_HANDLE virtual_handle = LoadHandle(tpm_handle); |
| std::string parameters; |
| Serialize_TPM_HANDLE(virtual_handle, ¶meters); |
| std::string command = CreateCommand(TPM_CC_FlushContext, kNoHandles, |
| kNoAuthorization, parameters); |
| std::string expected_parameters; |
| Serialize_TPM_HANDLE(tpm_handle, &expected_parameters); |
| std::string expected_command = CreateCommand( |
| TPM_CC_FlushContext, kNoHandles, kNoAuthorization, expected_parameters); |
| std::string response = CreateResponse(TPM_RC_SUCCESS, kNoHandles, |
| kNoAuthorization, kNoParameters); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(expected_command)) |
| .WillOnce(Return(response)); |
| std::string actual_response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(response, actual_response); |
| // Now we expect there to be no record of |virtual_handle|. |
| std::vector<TPM_HANDLE> input_handles = {virtual_handle}; |
| command = CreateCommand(TPM_CC_Sign, input_handles, kNoAuthorization, |
| kNoParameters); |
| response = CreateErrorResponse(TPM_RC_HANDLE | kResourceManagerTpmErrorBase); |
| actual_response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(response, actual_response); |
| |
| // Try again but attempt to flush |tpm_handle| instead of |virtual_handle|. |
| virtual_handle = LoadHandle(tpm_handle); |
| parameters.clear(); |
| Serialize_TPM_HANDLE(tpm_handle, ¶meters); |
| command = CreateCommand(TPM_CC_FlushContext, kNoHandles, kNoAuthorization, |
| parameters); |
| actual_response = resource_manager_.SendCommandAndWait(command); |
| // TPM_RC_HANDLE also expected here. |
| EXPECT_EQ(response, actual_response); |
| } |
| |
| TEST_F(ResourceManagerTest, VirtualHandleLoadBeforeUse) { |
| TPM_HANDLE tpm_handle = kArbitraryObjectHandle; |
| TPM_HANDLE virtual_handle = LoadHandle(tpm_handle); |
| EvictObjects(); |
| std::vector<TPM_HANDLE> input_handles = {virtual_handle}; |
| std::string command = CreateCommand(TPM_CC_Sign, input_handles, |
| kNoAuthorization, kNoParameters); |
| std::vector<TPM_HANDLE> expected_input_handles = {tpm_handle}; |
| std::string expected_command = CreateCommand( |
| TPM_CC_Sign, expected_input_handles, kNoAuthorization, kNoParameters); |
| std::string response = CreateResponse(TPM_RC_SUCCESS, kNoHandles, |
| kNoAuthorization, kNoParameters); |
| EXPECT_CALL(tpm_, ContextLoadSync(_, _, _)).WillOnce(Return(TPM_RC_SUCCESS)); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(expected_command)) |
| .WillOnce(Return(response)); |
| std::string actual_response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(response, actual_response); |
| } |
| |
| TEST_F(ResourceManagerTest, InvalidVirtualHandle) { |
| std::vector<TPM_HANDLE> input_handles = {kArbitraryObjectHandle}; |
| std::string command = CreateCommand(TPM_CC_Sign, input_handles, |
| kNoAuthorization, kNoParameters); |
| std::string response = |
| CreateErrorResponse(TPM_RC_HANDLE | kResourceManagerTpmErrorBase); |
| std::string actual_response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(response, actual_response); |
| } |
| |
| TEST_F(ResourceManagerTest, SimpleFuzzInputParser) { |
| std::vector<TPM_HANDLE> handles = {1, 2}; |
| std::string parameters = "12345"; |
| std::string command = |
| CreateCommand(TPM_CC_StartAuthSession, handles, |
| CreateCommandAuthorization(kArbitrarySessionHandle, |
| true), // continue_session |
| parameters); |
| // We don't care about what happens, only that it doesn't crash. |
| EXPECT_CALL(transceiver_, SendCommandAndWait(_)) |
| .WillRepeatedly(Return(CreateErrorResponse(TPM_RC_FAILURE))); |
| ScopedDisableLogging no_logging; |
| for (size_t i = 0; i < command.size(); ++i) { |
| resource_manager_.SendCommandAndWait(command.substr(0, i)); |
| resource_manager_.SendCommandAndWait(command.substr(i)); |
| std::string fuzzed_command(command); |
| for (uint8_t value = 0;; value++) { |
| fuzzed_command[i] = static_cast<char>(value); |
| resource_manager_.SendCommandAndWait(fuzzed_command); |
| if (value == 255) { |
| break; |
| } |
| } |
| } |
| } |
| |
| TEST_F(ResourceManagerTest, SimpleFuzzOutputParser) { |
| std::vector<TPM_HANDLE> handles = {1, 2}; |
| std::string parameters = "12345"; |
| std::string command = |
| CreateCommand(TPM_CC_StartAuthSession, handles, |
| CreateCommandAuthorization(kArbitrarySessionHandle, |
| true), // continue_session |
| parameters); |
| std::vector<TPM_HANDLE> out_handles = {3}; |
| std::string response = |
| CreateResponse(TPM_RC_SUCCESS, out_handles, |
| CreateResponseAuthorization(true), // continue_session |
| parameters); |
| std::string fuzzed_response; |
| EXPECT_CALL(transceiver_, SendCommandAndWait(_)) |
| .WillRepeatedly(ReturnPointee(&fuzzed_response)); |
| ScopedDisableLogging no_logging; |
| for (size_t i = 0; i < response.size(); ++i) { |
| fuzzed_response = response.substr(0, i); |
| resource_manager_.SendCommandAndWait(command); |
| fuzzed_response = response.substr(i); |
| resource_manager_.SendCommandAndWait(command); |
| fuzzed_response = response; |
| for (uint8_t value = 0;; value++) { |
| fuzzed_response[i] = static_cast<char>(value); |
| resource_manager_.SendCommandAndWait(command); |
| if (value == 255) { |
| break; |
| } |
| } |
| fuzzed_response[i] = response[i]; |
| } |
| } |
| |
| TEST_F(ResourceManagerTest, NewSession) { |
| StartSession(kArbitrarySessionHandle); |
| std::string command = |
| CreateCommand(TPM_CC_Startup, kNoHandles, |
| CreateCommandAuthorization(kArbitrarySessionHandle, |
| true), // continue_session |
| kNoParameters); |
| std::string response = |
| CreateResponse(TPM_RC_SUCCESS, kNoHandles, |
| CreateResponseAuthorization(true), // continue_session |
| kNoParameters); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(command)) |
| .WillOnce(Return(response)); |
| std::string actual_response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(response, actual_response); |
| } |
| |
| TEST_F(ResourceManagerTest, DiscontinuedSession) { |
| StartSession(kArbitrarySessionHandle); |
| // Use the session but do not continue. |
| std::string command = |
| CreateCommand(TPM_CC_Startup, kNoHandles, |
| CreateCommandAuthorization(kArbitrarySessionHandle, |
| false), // continue_session |
| kNoParameters); |
| std::string response = |
| CreateResponse(TPM_RC_SUCCESS, kNoHandles, |
| CreateResponseAuthorization(false), // continue_session |
| kNoParameters); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(command)) |
| .WillOnce(Return(response)); |
| std::string actual_response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(response, actual_response); |
| // Now attempt to use it again and expect a handle error. |
| response = CreateErrorResponse(TPM_RC_HANDLE | kResourceManagerTpmErrorBase); |
| actual_response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(response, actual_response); |
| } |
| |
| TEST_F(ResourceManagerTest, LoadSessionBeforeUse) { |
| StartSession(kArbitrarySessionHandle); |
| EvictSession(); |
| std::string command = |
| CreateCommand(TPM_CC_Startup, kNoHandles, |
| CreateCommandAuthorization(kArbitrarySessionHandle, |
| true), // continue_session |
| kNoParameters); |
| std::string response = |
| CreateResponse(TPM_RC_SUCCESS, kNoHandles, |
| CreateResponseAuthorization(true), // continue_session |
| kNoParameters); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(command)) |
| .WillOnce(Return(response)); |
| EXPECT_CALL(tpm_, ContextLoadSync(_, _, _)).WillOnce(Return(TPM_RC_SUCCESS)); |
| std::string actual_response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(response, actual_response); |
| } |
| |
| TEST_F(ResourceManagerTest, SessionHandleCleanup) { |
| StartSession(kArbitrarySessionHandle); |
| std::string parameters; |
| Serialize_TPM_HANDLE(kArbitrarySessionHandle, ¶meters); |
| std::string command = CreateCommand(TPM_CC_FlushContext, kNoHandles, |
| kNoAuthorization, parameters); |
| std::string response = CreateResponse(TPM_RC_SUCCESS, kNoHandles, |
| kNoAuthorization, kNoParameters); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(command)) |
| .WillOnce(Return(response)); |
| std::string actual_response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(response, actual_response); |
| // Now we expect there to be no record of |kArbitrarySessionHandle|. |
| command = CreateCommand(TPM_CC_Startup, kNoHandles, |
| CreateCommandAuthorization(kArbitrarySessionHandle, |
| true), // continue_session |
| kNoParameters); |
| response = CreateErrorResponse(TPM_RC_HANDLE | kResourceManagerTpmErrorBase); |
| actual_response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(response, actual_response); |
| } |
| |
| TEST_F(ResourceManagerTest, EvictWhenObjectInUse) { |
| TPM_HANDLE tpm_handle = kArbitraryObjectHandle; |
| TPM_HANDLE virtual_handle = LoadHandle(tpm_handle); |
| TPM_HANDLE tpm_handle2 = kArbitraryObjectHandle + 1; |
| LoadHandle(tpm_handle2); |
| std::vector<TPM_HANDLE> input_handles = {virtual_handle}; |
| std::string command = CreateCommand(TPM_CC_Sign, input_handles, |
| kNoAuthorization, kNoParameters); |
| // Trigger evict logic and verify |input_handles| are not evicted. |
| std::string response = CreateErrorResponse(TPM_RC_OBJECT_MEMORY); |
| std::string success_response = CreateResponse( |
| TPM_RC_SUCCESS, kNoHandles, kNoAuthorization, kNoParameters); |
| EXPECT_CALL(tpm_, ContextSaveSync(tpm_handle2, _, _, _)) |
| .WillOnce(Return(TPM_RC_SUCCESS)); |
| EXPECT_CALL(tpm_, FlushContextSync(tpm_handle2, _)) |
| .WillOnce(Return(TPM_RC_SUCCESS)); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(_)) |
| .WillOnce(Return(response)) |
| .WillRepeatedly(Return(success_response)); |
| std::string actual_response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(success_response, actual_response); |
| } |
| |
| TEST_F(ResourceManagerTest, EvictWhenSessionInUse) { |
| StartSession(kArbitrarySessionHandle); |
| StartSession(kArbitrarySessionHandle + 1); |
| std::string command = |
| CreateCommand(TPM_CC_Startup, kNoHandles, |
| CreateCommandAuthorization(kArbitrarySessionHandle, |
| true), // continue_session |
| kNoParameters); |
| std::string response = |
| CreateResponse(TPM_RC_SUCCESS, kNoHandles, |
| CreateResponseAuthorization(true), // continue_session |
| kNoParameters); |
| std::string error_response = CreateErrorResponse(TPM_RC_SESSION_MEMORY); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(_)) |
| .WillOnce(Return(error_response)) |
| .WillRepeatedly(Return(response)); |
| EXPECT_CALL(tpm_, ContextSaveSync(kArbitrarySessionHandle + 1, _, _, _)) |
| .WillOnce(Return(TPM_RC_SUCCESS)); |
| std::string actual_response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(response, actual_response); |
| } |
| |
| TEST_F(ResourceManagerTest, EvictMultipleObjects) { |
| const int kNumObjects = 10; |
| std::map<TPM_HANDLE, TPM_HANDLE> handles; |
| for (int i = 0; i < kNumObjects; ++i) { |
| TPM_HANDLE handle = kArbitraryObjectHandle + i; |
| handles[LoadHandle(handle)] = handle; |
| } |
| EvictObjects(); |
| std::string response = CreateResponse(TPM_RC_SUCCESS, kNoHandles, |
| kNoAuthorization, kNoParameters); |
| EXPECT_CALL(tpm_, ContextLoadSync(_, _, _)) |
| .Times(kNumObjects) |
| .WillRepeatedly(Return(TPM_RC_SUCCESS)); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(_)) |
| .WillRepeatedly(Return(response)); |
| for (auto item : handles) { |
| std::vector<TPM_HANDLE> input_handles = {item.first}; |
| std::string command = CreateCommand(TPM_CC_Sign, input_handles, |
| kNoAuthorization, kNoParameters); |
| std::string actual_response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(response, actual_response); |
| } |
| } |
| |
| TEST_F(ResourceManagerTest, EvictMostStaleSession) { |
| StartSession(kArbitrarySessionHandle); |
| StartSession(kArbitrarySessionHandle + 1); |
| StartSession(kArbitrarySessionHandle + 2); |
| std::string response = |
| CreateResponse(TPM_RC_SUCCESS, kNoHandles, |
| CreateResponseAuthorization(true), // continue_session |
| kNoParameters); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(_)) |
| .WillRepeatedly(Return(response)); |
| // Use the first two sessions, leaving the third as the most stale. |
| for (int i = 0; i < 2; ++i) { |
| std::string command = |
| CreateCommand(TPM_CC_Startup, kNoHandles, |
| CreateCommandAuthorization(kArbitrarySessionHandle + i, |
| true), // continue_session |
| kNoParameters); |
| std::string actual_response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(response, actual_response); |
| } |
| EvictSession(); |
| // EvictSession will have messed with the expectations; set them again. |
| EXPECT_CALL(transceiver_, SendCommandAndWait(_)) |
| .WillRepeatedly(Return(response)); |
| // Use the first two sessions again, expecting no calls to ContextLoad. |
| for (int i = 0; i < 2; ++i) { |
| std::string command = |
| CreateCommand(TPM_CC_Startup, kNoHandles, |
| CreateCommandAuthorization(kArbitrarySessionHandle + i, |
| true), // continue_session |
| kNoParameters); |
| std::string actual_response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(response, actual_response); |
| } |
| // Expect a call to ContextLoad if we use the third session. |
| std::string command = |
| CreateCommand(TPM_CC_Startup, kNoHandles, |
| CreateCommandAuthorization(kArbitrarySessionHandle + 2, |
| true), // continue_session |
| kNoParameters); |
| EXPECT_CALL(tpm_, ContextLoadSync(_, _, _)).WillOnce(Return(TPM_RC_SUCCESS)); |
| std::string actual_response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(response, actual_response); |
| } |
| |
| TEST_F(ResourceManagerTest, HandleContextGap) { |
| const int kNumSessions = 7; |
| const int kNumSessionsToUngap = 4; |
| std::vector<TPM_HANDLE> expected_ungap_order; |
| for (int i = 0; i < kNumSessions; ++i) { |
| StartSession(kArbitrarySessionHandle + i); |
| if (i < kNumSessionsToUngap) { |
| EvictSession(); |
| expected_ungap_order.push_back(kArbitrarySessionHandle + i); |
| } |
| } |
| // Invoke a context gap. |
| std::string command = CreateCommand(TPM_CC_Startup, kNoHandles, |
| kNoAuthorization, kNoParameters); |
| std::string response = CreateErrorResponse(TPM_RC_CONTEXT_GAP); |
| std::string success_response = CreateResponse( |
| TPM_RC_SUCCESS, kNoHandles, kNoAuthorization, kNoParameters); |
| { |
| InSequence ungap_order; |
| for (auto handle : expected_ungap_order) { |
| EXPECT_CALL(tpm_, ContextLoadSync(_, _, _)) |
| .WillOnce(Return(TPM_RC_SUCCESS)); |
| EXPECT_CALL(tpm_, ContextSaveSync(handle, _, _, _)) |
| .WillOnce(Return(TPM_RC_SUCCESS)); |
| } |
| } |
| EXPECT_CALL(transceiver_, SendCommandAndWait(_)) |
| .WillOnce(Return(response)) |
| .WillRepeatedly(Return(success_response)); |
| std::string actual_response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(success_response, actual_response); |
| } |
| |
| TEST_F(ResourceManagerTest, ExternalContext) { |
| StartSession(kArbitrarySessionHandle); |
| // Do an external context save. |
| std::vector<TPM_HANDLE> handles = {kArbitrarySessionHandle}; |
| std::string context_save = CreateCommand(TPM_CC_ContextSave, handles, |
| kNoAuthorization, kNoParameters); |
| std::string context_parameter1 = CreateContextParameter(1); |
| std::string context_save_response1 = CreateResponse( |
| TPM_RC_SUCCESS, kNoHandles, kNoAuthorization, context_parameter1); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(context_save)) |
| .WillOnce(Return(context_save_response1)); |
| std::string actual_response = |
| resource_manager_.SendCommandAndWait(context_save); |
| EXPECT_EQ(context_save_response1, actual_response); |
| |
| // Invoke a context gap (which will cause context1 to be mapped to context2). |
| EXPECT_CALL(tpm_, |
| ContextLoadSync(Field(&TPMS_CONTEXT::sequence, Eq(1u)), _, _)) |
| .WillOnce(Return(TPM_RC_SUCCESS)); |
| EXPECT_CALL(tpm_, ContextSaveSync(kArbitrarySessionHandle, _, _, _)) |
| .WillOnce(DoAll(SetArgumentPointee<2>(CreateContext(2)), |
| Return(TPM_RC_SUCCESS))); |
| std::string command = CreateCommand(TPM_CC_Startup, kNoHandles, |
| kNoAuthorization, kNoParameters); |
| std::string response = CreateErrorResponse(TPM_RC_CONTEXT_GAP); |
| std::string success_response = CreateResponse( |
| TPM_RC_SUCCESS, kNoHandles, kNoAuthorization, kNoParameters); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(command)) |
| .WillOnce(Return(response)) |
| .WillOnce(Return(success_response)); |
| actual_response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(success_response, actual_response); |
| |
| // Now load external context1 and expect an actual load of context2. |
| std::string context_load1 = CreateCommand( |
| TPM_CC_ContextLoad, kNoHandles, kNoAuthorization, context_parameter1); |
| std::string context_load2 = |
| CreateCommand(TPM_CC_ContextLoad, kNoHandles, kNoAuthorization, |
| CreateContextParameter(2)); |
| std::string context_load_response = |
| CreateResponse(TPM_RC_SUCCESS, handles, kNoAuthorization, kNoParameters); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(context_load2)) |
| .WillOnce(Return(context_load_response)); |
| actual_response = resource_manager_.SendCommandAndWait(context_load1); |
| EXPECT_EQ(context_load_response, actual_response); |
| } |
| |
| TEST_F(ResourceManagerTest, NestedFailures) { |
| // The scenario being tested is when a command results in a warning to be |
| // handled by the resource manager, and in the process of handling the first |
| // warning another warning occurs which should be handled by the resource |
| // manager, etc.. |
| for (int i = 0; i < 3; ++i) { |
| LoadHandle(kArbitraryObjectHandle + i); |
| } |
| EvictObjects(); |
| for (int i = 3; i < 6; ++i) { |
| LoadHandle(kArbitraryObjectHandle + i); |
| } |
| for (int i = 0; i < 10; ++i) { |
| StartSession(kArbitrarySessionHandle + i); |
| EvictSession(); |
| } |
| for (int i = 10; i < 20; ++i) { |
| StartSession(kArbitrarySessionHandle + i); |
| } |
| std::string error_response = CreateErrorResponse(TPM_RC_MEMORY); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(_)) |
| .WillRepeatedly(Return(error_response)); |
| // The TPM_RC_MEMORY will result in a context save, make that fail too. |
| EXPECT_CALL(tpm_, ContextSaveSync(_, _, _, _)) |
| .WillRepeatedly(Return(TPM_RC_CONTEXT_GAP)); |
| // The TPM_RC_CONTEXT_GAP will result in a context load. |
| EXPECT_CALL(tpm_, ContextLoadSync(_, _, _)) |
| .WillRepeatedly(Return(TPM_RC_SESSION_HANDLES)); |
| // The TPM_RC_SESSION_HANDLES will result in a context flush. |
| EXPECT_CALL(tpm_, FlushContextSync(_, _)) |
| .WillRepeatedly(Return(TPM_RC_SESSION_MEMORY)); |
| // The resource manager should not handle the same warning twice so we expect |
| // the error of the original call to bubble up. |
| std::string command = CreateCommand(TPM_CC_Startup, kNoHandles, |
| kNoAuthorization, kNoParameters); |
| std::string response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(error_response, response); |
| } |
| |
| TEST_F(ResourceManagerTest, OutOfMemory) { |
| std::string error_response = CreateErrorResponse(TPM_RC_MEMORY); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(_)) |
| .WillRepeatedly(Return(error_response)); |
| std::string command = CreateCommand(TPM_CC_Startup, kNoHandles, |
| kNoAuthorization, kNoParameters); |
| std::string response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(error_response, response); |
| } |
| |
| TEST_F(ResourceManagerTest, ReentrantFixGap) { |
| for (int i = 0; i < 3; ++i) { |
| StartSession(kArbitrarySessionHandle + i); |
| EvictSession(); |
| } |
| for (int i = 3; i < 6; ++i) { |
| StartSession(kArbitrarySessionHandle + i); |
| } |
| std::string error_response = CreateErrorResponse(TPM_RC_CONTEXT_GAP); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(_)) |
| .WillRepeatedly(Return(error_response)); |
| EXPECT_CALL(tpm_, ContextSaveSync(_, _, _, _)) |
| .WillRepeatedly(Return(TPM_RC_CONTEXT_GAP)); |
| EXPECT_CALL(tpm_, ContextLoadSync(_, _, _)) |
| .WillOnce(Return(TPM_RC_CONTEXT_GAP)) |
| .WillRepeatedly(Return(TPM_RC_SUCCESS)); |
| std::string command = CreateCommand(TPM_CC_Startup, kNoHandles, |
| kNoAuthorization, kNoParameters); |
| std::string response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(error_response, response); |
| } |
| |
| TEST_F(ResourceManagerTest, PasswordAuthorization) { |
| std::string command = |
| CreateCommand(TPM_CC_Startup, kNoHandles, |
| CreateCommandAuthorization(TPM_RS_PW, |
| false), // continue_session |
| kNoParameters); |
| std::string response = |
| CreateResponse(TPM_RC_SUCCESS, kNoHandles, |
| CreateResponseAuthorization(false), // continue_session |
| kNoParameters); |
| EXPECT_CALL(transceiver_, SendCommandAndWait(command)) |
| .WillOnce(Return(response)); |
| std::string actual_response = resource_manager_.SendCommandAndWait(command); |
| EXPECT_EQ(response, actual_response); |
| } |
| |
| } // namespace trunks |