text: //
  // 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 <algorithm>
  #include <map>
  #include <set>
  #include <string>
  #include <vector>
  
  #include <base/callback.h>
  
  #include "trunks/error_codes.h"
  
  namespace {
  
  const int kMaxCommandAttempts = 3;
  const size_t kMinimumAuthorizationSize = 9;
  const size_t kMessageHeaderSize = 10;
  const trunks::TPM_HANDLE kMaxVirtualHandle =
      (trunks::HR_TRANSIENT + trunks::HR_HANDLE_MASK);
  
  class ScopedBool {
   public:
    ScopedBool() : target_(nullptr) {}
    ~ScopedBool() {
      if (target_) {
        *target_ = false;
      }
    }
    void Enable(bool* target) {
      target_ = target;
      *target_ = true;
    }
   private:
    bool* target_;
  };
  
  }  // namespace
  
  namespace trunks {
  
  ResourceManager::ResourceManager(const TrunksFactory& factory,
                                   CommandTransceiver* next_transceiver)
      : factory_(factory),
        next_transceiver_(next_transceiver) {}
  
  ResourceManager::~ResourceManager() {}
  
  void ResourceManager::Initialize() {
    TPM_RC result = factory_.GetTpm()->StartupSync(TPM_SU_CLEAR, nullptr);
    // Ignore TPM_RC_INITIALIZE, that means it was already started.
    CHECK(result == TPM_RC_SUCCESS || result == TPM_RC_INITIALIZE)
        << "TPM startup failure: " << GetErrorString(result);
    result = factory_.GetTpm()->SelfTestSync(YES /* Full test. */, nullptr);
    CHECK_EQ(result, TPM_RC_SUCCESS) << "TPM self-test failure: "
                                     << GetErrorString(result);
    // Full control of the TPM is assumed and required. Existing transient object
    // and session handles are mercilessly flushed.
    for (UINT32 handle_type : {HR_TRANSIENT,
                               HR_HMAC_SESSION,
                               HR_POLICY_SESSION}) {
      TPMI_YES_NO more_data = YES;
      TPMS_CAPABILITY_DATA data;
      UINT32 handle_range = handle_type;
      while (more_data) {
        result = factory_.GetTpm()->GetCapabilitySync(TPM_CAP_HANDLES,
                                                      handle_range,
                                                      MAX_CAP_HANDLES,
                                                      &more_data,
                                                      &data,
                                                      nullptr);
        if (result != TPM_RC_SUCCESS) {
          LOG(WARNING) << "Failed to query existing handles: "
                       << GetErrorString(result);
          break;
        }
        const TPML_HANDLE& handle_list = data.data.handles;
        for (UINT32 i = 0; i < handle_list.count; ++i) {
          factory_.GetTpm()->FlushContextSync(handle_list.handle[i], nullptr);
        }
        if (more_data) {
          // Adjust the range to be greater than the most recent handle so on the
          // next query we'll start where we left off.
          handle_range = handle_list.handle[handle_list.count-1];
        }
      }
    }
  }
  
  void ResourceManager::SendCommand(
      const std::string& command,
      const ResponseCallback& callback) {
    callback.Run(SendCommandAndWait(command));
  }
  
  std::string ResourceManager::SendCommandAndWait(const std::string& command) {
    // Sanitize the |command|. If this succeeds consistency of the command header
    // and the size of all other sections can be assumed.
    MessageInfo command_info;
    TPM_RC result = ParseCommand(command, &command_info);
    if (result != TPM_RC_SUCCESS) {
      return CreateErrorResponse(result);
    }
    // A special case for FlushContext. It requires special handling because it
    // has a handle as a parameter and because we need to cleanup if it succeeds.
    if (command_info.code == TPM_CC_FlushContext) {
      return ProcessFlushContext(command, command_info);
    }
    // Process all the input handles, e.g. map virtual handles.
    std::vector<TPM_HANDLE> updated_handles;
    for (auto handle : command_info.handles) {
      TPM_HANDLE tpm_handle;
      result = ProcessInputHandle(command_info, handle, &tpm_handle);
      if (result != TPM_RC_SUCCESS) {
        return CreateErrorResponse(result);
      }
      updated_handles.push_back(tpm_handle);
    }
    std::string updated_command = ReplaceHandles(command, updated_handles);
    // Make sure all the required sessions are loaded.
    for (auto handle : command_info.session_handles) {
      result = EnsureSessionIsLoaded(command_info, handle);
      if (result != TPM_RC_SUCCESS) {
        return CreateErrorResponse(result);
      }
    }
    // On a ContextLoad we may need to map virtualized context data.
    if (command_info.code == TPM_CC_ContextLoad) {
      std::string actual_load_data = GetActualContextFromExternalContext(
          command_info.parameter_data);
      // Check equality to see if replacement is necessary, and check size to see
      // if the command looks like we expect (the idea is to avoid 'fixing'
      // malformed commands). Note: updated_command.size() is guaranteed to be >=
      // kMessageHeaderSize based on the sanitization in ParseCommand.
      if (actual_load_data != command_info.parameter_data &&
          actual_load_data.size() ==
              updated_command.size() - kMessageHeaderSize) {
        // Replace the parameter section of the command with |actual_load_data|.
        VLOG(1) << "REPLACE_EXTERNAL_CONTEXT";
        updated_command.replace(kMessageHeaderSize, std::string::npos,
                                actual_load_data);
      }
    }
    // Send the |updated_command| to the next layer. Attempt to fix any actionable
    // warnings for up to kMaxCommandAttempts.
    std::string response;
    MessageInfo response_info;
    int attempts = 0;
    while (attempts++ < kMaxCommandAttempts) {
      response = next_transceiver_->SendCommandAndWait(updated_command);
      result = ParseResponse(command_info, response, &response_info);
      if (result != TPM_RC_SUCCESS) {
        return CreateErrorResponse(result);
      }
      if (!FixWarnings(command_info, response_info.code)) {
        // No actionable warnings were handled.
        break;
      }
    }
    if (response_info.code == TPM_RC_SUCCESS) {
      if (response_info.session_continued.size() !=
          command_info.session_handles.size()) {
        LOG(WARNING) << "Session count mismatch!";
      }
      // Cleanup any sessions that were not continued.
      for (size_t i = 0; i < command_info.session_handles.size(); ++i) {
        if (i < response_info.session_continued.size() &&
            !response_info.session_continued[i]) {
          CleanupFlushedHandle(command_info.session_handles[i]);
        }
      }
      // On a successful context save we need to cache the context data in case it
      // needs to be virtualized later.
      if (command_info.code == TPM_CC_ContextSave) {
        ProcessExternalContextSave(command_info, response_info);
      }
      // Process all the output handles, which is loosely the inverse of the input
      // handle processing. E.g. virtualize handles.
      std::vector<TPM_HANDLE> virtual_handles;
      for (auto handle : response_info.handles) {
        virtual_handles.push_back(ProcessOutputHandle(handle));
      }
      response = ReplaceHandles(response, virtual_handles);
    }
    return response;
  }
  
  bool ResourceManager::ChooseSessionToEvict(
      const std::vector<TPM_HANDLE>& sessions_to_retain,
      TPM_HANDLE* session_to_evict) {
    // Build a list of candidates by excluding |sessions_to_retain|.
    std::vector<TPM_HANDLE> candidates;
    for (auto& item : session_handles_) {
      HandleInfo& info = item.second;
      if (info.is_loaded &&
          std::find(sessions_to_retain.begin(),
                    sessions_to_retain.end(),
                    info.tpm_handle) == sessions_to_retain.end()) {
        candidates.push_back(item.first);
      }
    }
    if (candidates.empty()) {
      LOG(WARNING) << "No sessions to evict.";
      return false;
    }
    // Choose the candidate with the earliest |time_of_last_use|.
    auto oldest_iter = std::min_element(
        candidates.begin(), candidates.end(),
        [this](TPM_HANDLE a, TPM_HANDLE b) {
          return (session_handles_[a].time_of_last_use <
                  session_handles_[b].time_of_last_use);
        });
    *session_to_evict = *oldest_iter;
    return true;
  }
  
  void ResourceManager::CleanupFlushedHandle(TPM_HANDLE flushed_handle) {
    if (IsObjectHandle(flushed_handle)) {
      // For transient object handles, remove both the actual and virtual handles.
      if (virtual_object_handles_.count(flushed_handle) > 0) {
        tpm_object_handles_.erase(
            virtual_object_handles_[flushed_handle].tpm_handle);
        virtual_object_handles_.erase(flushed_handle);
      }
    } else if (IsSessionHandle(flushed_handle)) {
      auto iter = session_handles_.find(flushed_handle);
      if (iter == session_handles_.end()) {
        return;
      }
      // For session handles, remove the handle and any associated context data.
      HandleInfo& info = iter->second;
      if (!info.is_loaded) {
        std::string actual_context_data;
        Serialize_TPMS_CONTEXT(info.context, &actual_context_data);
        if (actual_context_to_external_.count(actual_context_data) > 0) {
          external_context_to_actual_.erase(
              actual_context_to_external_[actual_context_data]);
          actual_context_to_external_.erase(actual_context_data);
        }
      }
      session_handles_.erase(flushed_handle);
      VLOG(1) << "CLEANUP_SESSION: " << std::hex << flushed_handle;
    }
  }
  
  TPM_HANDLE ResourceManager::CreateVirtualHandle() {
    TPM_HANDLE handle;
    do {
      handle = next_virtual_handle_;
      if (next_virtual_handle_ == kMaxVirtualHandle) {
        next_virtual_handle_ = TRANSIENT_FIRST;
      } else {
        ++next_virtual_handle_;
      }
    } while (virtual_object_handles_.count(handle) > 0);
    return handle;
  }
  
  TPM_RC ResourceManager::EnsureSessionIsLoaded(const MessageInfo& command_info,
                                                TPM_HANDLE session_handle) {
    // A password authorization can skip all this.
    if (session_handle == TPM_RS_PW) {
      return TPM_RC_SUCCESS;
    }
    auto handle_iter = session_handles_.find(session_handle);
    if (handle_iter == session_handles_.end()) {
      return MakeError(TPM_RC_HANDLE, FROM_HERE);
    }
    HandleInfo& handle_info = handle_iter->second;
    if (!handle_info.is_loaded) {
      TPM_RC result = LoadContext(command_info, &handle_info);
      if (result != TPM_RC_SUCCESS) {
        return result;
      }
      VLOG(1) << "RELOAD_SESSION: " << std::hex << session_handle;
    }
    handle_info.time_of_last_use = base::TimeTicks::Now();
    return TPM_RC_SUCCESS;
  }
  
  void ResourceManager::EvictObjects(const MessageInfo& command_info) {
    for (auto& item : virtual_object_handles_) {
      HandleInfo& info = item.second;
      if (!info.is_loaded ||
          std::find(command_info.handles.begin(),
                    command_info.handles.end(),
                    item.first) != command_info.handles.end()) {
        continue;
      }
      TPM_RC result = SaveContext(command_info, &info);
      if (result != TPM_RC_SUCCESS) {
        LOG(WARNING) << "Failed to save transient object: "
                     << GetErrorString(result);
        continue;
      }
      result = factory_.GetTpm()->FlushContextSync(info.tpm_handle, nullptr);
      if (result != TPM_RC_SUCCESS) {
        LOG(WARNING) << "Failed to evict transient object: "
                     << GetErrorString(result);
        continue;
      }
      tpm_object_handles_.erase(info.tpm_handle);
      VLOG(1) << "EVICT_OBJECT: " << std::hex << info.tpm_handle;
    }
  }
  
  void ResourceManager::EvictSession(const MessageInfo& command_info) {
    TPM_HANDLE session_to_evict;
    if (!ChooseSessionToEvict(command_info.session_handles, &session_to_evict)) {
      return;
    }
    HandleInfo& info = session_handles_[session_to_evict];
    TPM_RC result = SaveContext(command_info, &info);
    if (result != TPM_RC_SUCCESS) {
      LOG(WARNING) << "Failed to evict session: " << GetErrorString(result);
    }
    VLOG(1) << "EVICT_SESSION: " << std::hex << session_to_evict;
  }
  
  std::vector<TPM_HANDLE> ResourceManager::ExtractHandlesFromBuffer(
      size_t number_of_handles,
      std::string* buffer) {
    std::vector<TPM_HANDLE> handles(number_of_handles);
    for (size_t i = 0; i < number_of_handles; ++i) {
      TPM_HANDLE handle;
      if (TPM_RC_SUCCESS == Parse_TPM_HANDLE(buffer, &handle, nullptr)) {
        handles[i] = handle;
      }
    }
    return handles;
  }
  
  void ResourceManager::FixContextGap(const MessageInfo& command_info) {
    std::vector<TPM_HANDLE> sessions_to_ungap;
    for (const auto& item : session_handles_) {
      const HandleInfo& info = item.second;
      if (!info.is_loaded) {
        sessions_to_ungap.push_back(item.first);
      }
    }
    // Sort by |time_of_create|.
    std::sort(sessions_to_ungap.begin(), sessions_to_ungap.end(),
              [this](TPM_HANDLE a, TPM_HANDLE b) {
                return (session_handles_[a].time_of_create <
                        session_handles_[b].time_of_create);
              });
    for (auto handle : sessions_to_ungap) {
      HandleInfo& info = session_handles_[handle];
      // Loading and re-saving allows the TPM to assign a new context counter.
      std::string old_context_blob;
      Serialize_TPMS_CONTEXT(info.context, &old_context_blob);
      TPM_RC result = LoadContext(command_info, &info);
      if (result != TPM_RC_SUCCESS) {
        LOG(WARNING) << "Failed to un-gap session (load): "
                     << GetErrorString(result);
        continue;
      }
      result = SaveContext(command_info, &info);
      if (result != TPM_RC_SUCCESS) {
        LOG(WARNING) << "Failed to un-gap session (save): "
                     << GetErrorString(result);
        continue;
      }
      // If this context is one that we're tracking for external use, update it.
      auto iter = actual_context_to_external_.find(old_context_blob);
      if (iter == actual_context_to_external_.end()) {
        continue;
      }
      std::string new_context_blob;
      Serialize_TPMS_CONTEXT(info.context, &new_context_blob);
      const std::string& external_context_blob = iter->second;
      actual_context_to_external_[new_context_blob] = external_context_blob;
      external_context_to_actual_[external_context_blob] = new_context_blob;
      actual_context_to_external_.erase(old_context_blob);
    }
  }
  
  bool ResourceManager::FixWarnings(const MessageInfo& command_info,
                                    TPM_RC result) {
    if ((result & RC_WARN) == 0) {
      return false;
    }
    // This method can be called anytime without tracking whether the current
    // operation is already an attempt to fix a warning. All re-entrance issues
    // are dealt with here using the following rule: Never attempt to fix the same
    // warning twice.
    ScopedBool scoped_bool;
    if (!fixing_warnings_) {
      scoped_bool.Enable(&fixing_warnings_);
      warnings_already_seen_.clear();
    } else if (warnings_already_seen_.count(result) > 0) {
      return false;
    }
    warnings_already_seen_.insert(result);
    switch (result) {
      case TPM_RC_CONTEXT_GAP:
        FixContextGap(command_info);
        return true;
      case TPM_RC_OBJECT_MEMORY:
      case TPM_RC_OBJECT_HANDLES:
        EvictObjects(command_info);
        return true;
      case TPM_RC_SESSION_MEMORY:
        EvictSession(command_info);
        return true;
      case TPM_RC_MEMORY:
        EvictObjects(command_info);
        EvictSession(command_info);
        return true;
      case TPM_RC_SESSION_HANDLES:
        FlushSession(command_info);
        return true;
    }
    return false;
  }
  
  void ResourceManager::FlushSession(const MessageInfo& command_info) {
    TPM_HANDLE session_to_flush;
    LOG(WARNING) << "Resource manager needs to flush a session.";
    if (!ChooseSessionToEvict(command_info.session_handles, &session_to_flush)) {
      return;
    }
    TPM_RC result = factory_.GetTpm()->FlushContextSync(session_to_flush,
                                                        nullptr);
    if (result != TPM_RC_SUCCESS) {
      LOG(WARNING) << "Failed to flush session: " << GetErrorString(result);
      return;
    }
    CleanupFlushedHandle(session_to_flush);
  }
  
  std::string ResourceManager::GetActualContextFromExternalContext(
      const std::string& external_context) {
    auto iter = external_context_to_actual_.find(external_context);
    if (iter == external_context_to_actual_.end()) {
      return external_context;
    }
    return iter->second;
  }
  
  bool ResourceManager::IsObjectHandle(TPM_HANDLE handle) const {
    return ((handle & HR_RANGE_MASK) == HR_TRANSIENT);
  }
  
  bool ResourceManager::IsSessionHandle(TPM_HANDLE handle) const {
    return ((handle & HR_RANGE_MASK) == HR_HMAC_SESSION ||
            (handle & HR_RANGE_MASK) == HR_POLICY_SESSION);
  }
  
  TPM_RC ResourceManager::LoadContext(const MessageInfo& command_info,
                                      HandleInfo* handle_info) {
    CHECK(!handle_info->is_loaded);
    TPM_RC result = TPM_RC_SUCCESS;
    int attempts = 0;
    while (attempts++ < kMaxCommandAttempts) {
      result = factory_.GetTpm()->ContextLoadSync(handle_info->context,
                                                  &handle_info->tpm_handle,
                                                  nullptr);
      if (!FixWarnings(command_info, result)) {
        break;
      }
    }
    if (result != TPM_RC_SUCCESS) {
      LOG(ERROR) << __func__ << ": Failed to load context: "
                 << GetErrorString(result);
      return result;
    }
    handle_info->is_loaded = true;
    return result;
  }
  
  TPM_RC ResourceManager::MakeError(TPM_RC tpm_error,
                                    const ::tracked_objects::Location& location) {
    LOG(ERROR) << "ResourceManager::" << location.function_name() << ":"
               << location.line_number() << ": " << GetErrorString(tpm_error);
    return tpm_error + kResourceManagerTpmErrorBase;
  }
  
  TPM_RC ResourceManager::ParseCommand(const std::string& command,
                                       MessageInfo* command_info) {
    CHECK(command_info);
    std::string buffer = command;
    TPM_ST tag;
    TPM_RC result = Parse_TPM_ST(&buffer, &tag, nullptr);
    if (result != TPM_RC_SUCCESS) {
      return MakeError(result, FROM_HERE);
    }
    if (tag != TPM_ST_SESSIONS && tag != TPM_ST_NO_SESSIONS) {
      return MakeError(TPM_RC_TAG, FROM_HERE);
    }
    command_info->has_sessions = (tag == TPM_ST_SESSIONS);
  
    UINT32 size = 0;
    result = Parse_UINT32(&buffer, &size, nullptr);
    if (result != TPM_RC_SUCCESS) {
      return MakeError(result, FROM_HERE);
    }
    if (size != command.size()) {
      return MakeError(TPM_RC_SIZE, FROM_HERE);
    }
  
    result = Parse_TPM_CC(&buffer, &command_info->code, nullptr);
    if (result != TPM_RC_SUCCESS) {
      return MakeError(result, FROM_HERE);
    }
    if (command_info->code < TPM_CC_FIRST || command_info->code > TPM_CC_LAST) {
      return MakeError(TPM_RC_COMMAND_CODE, FROM_HERE);
    }
  
    size_t number_of_handles = GetNumberOfRequestHandles(command_info->code);
    command_info->handles = ExtractHandlesFromBuffer(number_of_handles, &buffer);
    if (number_of_handles != command_info->handles.size()) {
      return MakeError(TPM_RC_SIZE, FROM_HERE);
    }
    if (command_info->has_sessions) {
      // Sessions exist, so we're expecting a valid authorization size value.
      UINT32 authorization_size = 0;
      result = Parse_UINT32(&buffer, &authorization_size, nullptr);
      if (result != TPM_RC_SUCCESS) {
        return MakeError(result, FROM_HERE);
      }
      if (buffer.size() < authorization_size ||
          authorization_size < kMinimumAuthorizationSize) {
        return MakeError(TPM_RC_SIZE, FROM_HERE);
      }
      // Move out the parameter bytes, leaving only the authorization section.
      command_info->parameter_data = buffer.substr(authorization_size);
      buffer.erase(authorization_size);
      // Parse as many authorization sessions as there are in the section.
      while (!buffer.empty()) {
        TPM_HANDLE handle;
        result = Parse_TPM_HANDLE(&buffer, &handle, nullptr);
        if (result != TPM_RC_SUCCESS) {
          return MakeError(result, FROM_HERE);
        }
        if (handle != TPM_RS_PW && session_handles_.count(handle) == 0) {
          return MakeError(TPM_RC_HANDLE, FROM_HERE);
        }
        TPM2B_NONCE nonce;
        result = Parse_TPM2B_NONCE(&buffer, &nonce, nullptr);
        if (result != TPM_RC_SUCCESS) {
          return MakeError(result, FROM_HERE);
        }
        BYTE attributes;
        result = Parse_BYTE(&buffer, &attributes, nullptr);
        if (result != TPM_RC_SUCCESS) {
          return MakeError(result, FROM_HERE);
        }
        TPM2B_DIGEST authorization;
        result = Parse_TPM2B_DIGEST(&buffer, &authorization, nullptr);
        if (result != TPM_RC_SUCCESS) {
          return MakeError(result, FROM_HERE);
        }
        command_info->session_handles.push_back(handle);
        command_info->session_continued.push_back((attributes & 1) == 1);
      }
    } else {
      // No sessions, so all remaining data is parameter data.
      command_info->parameter_data = buffer;
    }
    return TPM_RC_SUCCESS;
  }
  
  TPM_RC ResourceManager::ParseResponse(const MessageInfo& command_info,
                                        const std::string& response,
                                        MessageInfo* response_info) {
    CHECK(response_info);
    std::string buffer = response;
    TPM_ST tag;
    TPM_RC result = Parse_TPM_ST(&buffer, &tag, nullptr);
    if (result != TPM_RC_SUCCESS) {
      return MakeError(result, FROM_HERE);
    }
    if (tag != TPM_ST_SESSIONS && tag != TPM_ST_NO_SESSIONS) {
      return MakeError(TPM_RC_TAG, FROM_HERE);
    }
    response_info->has_sessions = (tag == TPM_ST_SESSIONS);
  
    UINT32 size = 0;
    result = Parse_UINT32(&buffer, &size, nullptr);
    if (result != TPM_RC_SUCCESS) {
      return MakeError(result, FROM_HERE);
    }
    if (size != response.size()) {
      return MakeError(TPM_RC_SIZE, FROM_HERE);
    }
  
    result = Parse_TPM_RC(&buffer, &response_info->code, nullptr);
    if (result != TPM_RC_SUCCESS) {
      return MakeError(result, FROM_HERE);
    }
  
    size_t number_of_handles = GetNumberOfResponseHandles(command_info.code);
    response_info->handles = ExtractHandlesFromBuffer(number_of_handles, &buffer);
    if (number_of_handles != response_info->handles.size()) {
      return MakeError(TPM_RC_SIZE, FROM_HERE);
    }
    if (response_info->has_sessions) {
      // Sessions exist, so we're expecting a valid parameter size value.
      UINT32 parameter_size = 0;
      result = Parse_UINT32(&buffer, &parameter_size, nullptr);
      if (result != TPM_RC_SUCCESS) {
        return MakeError(result, FROM_HERE);
      }
      if (buffer.size() < parameter_size) {
        return MakeError(TPM_RC_SIZE, FROM_HERE);
      }
      // Move out the parameter bytes, leaving only the authorization section.
      response_info->parameter_data = buffer.substr(0, parameter_size);
      buffer.erase(0, parameter_size);
      // Parse as many authorization sessions as there are in the section.
      while (!buffer.empty()) {
        TPM2B_NONCE nonce;
        result = Parse_TPM2B_NONCE(&buffer, &nonce, nullptr);
        if (result != TPM_RC_SUCCESS) {
          return MakeError(result, FROM_HERE);
        }
        BYTE attributes;
        result = Parse_BYTE(&buffer, &attributes, nullptr);
        if (result != TPM_RC_SUCCESS) {
          return MakeError(result, FROM_HERE);
        }
        TPM2B_DIGEST acknowledgement;
        result = Parse_TPM2B_DIGEST(&buffer, &acknowledgement, nullptr);
        if (result != TPM_RC_SUCCESS) {
          return MakeError(result, FROM_HERE);
        }
        response_info->session_continued.push_back((attributes & 1) == 1);
      }
    } else {
      // No sessions, so all remaining data is parameter data.
      response_info->parameter_data = buffer;
    }
    return TPM_RC_SUCCESS;
  }
  
  void ResourceManager::ProcessExternalContextSave(
      const MessageInfo& command_info,
      const MessageInfo& response_info) {
    CHECK_EQ(command_info.code, TPM_CC_ContextSave);
    if (command_info.handles.size() != 1) {
      LOG(WARNING) << "Invalid context save command.";
      return;
    }
    // We know command_info.handles[0] is valid because this is validated when the
    // command is parsed.
    TPM_HANDLE saved_handle = command_info.handles[0];
    // Only track external context data for session handles.
    if (!IsSessionHandle(saved_handle)) {
      return;
    }
    std::string mutable_parameter = response_info.parameter_data;
    TPMS_CONTEXT context;
    std::string context_blob;
    TPM_RC result = Parse_TPMS_CONTEXT(&mutable_parameter,
                                       &context,
                                       &context_blob);
    if (result != TPM_RC_SUCCESS) {
      LOG(WARNING) << "Invalid context save response: " << GetErrorString(result);
      return;
    }
    auto iter = session_handles_.find(saved_handle);
    if (iter != session_handles_.end()) {
      iter->second.is_loaded = false;
      iter->second.context = context;
    } else {
      // Unknown handle? Not anymore.
      LOG(WARNING) << "Context for unknown handle.";
      HandleInfo new_handle_info;
      new_handle_info.Init(saved_handle);
      new_handle_info.is_loaded = false;
      new_handle_info.context = context;
      session_handles_[saved_handle] = new_handle_info;
    }
    // Use the original context data as the 'external' context data. If this gets
    // virtualized, only the 'actual' context data will change.
    external_context_to_actual_[context_blob] = context_blob;
    actual_context_to_external_[context_blob] = context_blob;
  }
  
  std::string ResourceManager::ProcessFlushContext(
      const std::string& command,
      const MessageInfo& command_info) {
    std::string buffer = command_info.parameter_data;
    // There must be exactly one handle in the parameters section.
    std::vector<TPM_HANDLE> handles = ExtractHandlesFromBuffer(1, &buffer);
    if (handles.size() != 1) {
      return CreateErrorResponse(MakeError(TPM_RC_SIZE, FROM_HERE));
    }
    TPM_HANDLE handle = handles[0];
    TPM_HANDLE actual_handle = handle;
    if (IsObjectHandle(handle)) {
      auto iter = virtual_object_handles_.find(handle);
      if (iter == virtual_object_handles_.end()) {
        return CreateErrorResponse(MakeError(TPM_RC_HANDLE, FROM_HERE));
      }
      if (!iter->second.is_loaded) {
        // The handle wasn't loaded so no need to bother the TPM.
        CleanupFlushedHandle(handle);
        return CreateErrorResponse(TPM_RC_SUCCESS);
      }
      actual_handle = iter->second.tpm_handle;
    }
    // Send a command with the original header but with |actual_handle| as the
    // parameter.
    std::string handle_blob;
    Serialize_TPM_HANDLE(actual_handle, &handle_blob);
    std::string updated_command = command.substr(0, kMessageHeaderSize) +
                                  handle_blob;
    // No need to loop and fix warnings, there are no actionable warnings on when
    // flushing context.
    std::string response = next_transceiver_->SendCommandAndWait(updated_command);
    MessageInfo response_info;
    TPM_RC result = ParseResponse(command_info, response, &response_info);
    if (result != TPM_RC_SUCCESS) {
      return CreateErrorResponse(result);
    }
    // Cleanup the handle locally even if the TPM did not recognize it.
    if (response_info.code == TPM_RC_SUCCESS ||
        response_info.code == TPM_RC_HANDLE) {
      CleanupFlushedHandle(handle);
    }
    return response;
  }
  
  TPM_RC ResourceManager::ProcessInputHandle(const MessageInfo& command_info,
                                             TPM_HANDLE virtual_handle,
                                             TPM_HANDLE* actual_handle) {
    // Only transient object handles are virtualized.
    if (!IsObjectHandle(virtual_handle)) {
      *actual_handle = virtual_handle;
      return TPM_RC_SUCCESS;
    }
    auto handle_iter = virtual_object_handles_.find(virtual_handle);
    if (handle_iter == virtual_object_handles_.end()) {
      return MakeError(TPM_RC_HANDLE, FROM_HERE);
    }
    HandleInfo& handle_info = handle_iter->second;
    if (!handle_info.is_loaded) {
      TPM_RC result = LoadContext(command_info, &handle_info);
      if (result != TPM_RC_SUCCESS) {
        return result;
      }
      tpm_object_handles_[handle_info.tpm_handle] = virtual_handle;
      VLOG(1) << "RELOAD_OBJECT: " << std::hex << virtual_handle;
    }
    VLOG(1) << "INPUT_HANDLE_REPLACE: " << std::hex << virtual_handle << " -> "
            << std::hex << handle_info.tpm_handle;
    *actual_handle = handle_info.tpm_handle;
    return TPM_RC_SUCCESS;
  }
  
  TPM_HANDLE ResourceManager::ProcessOutputHandle(TPM_HANDLE handle) {
    // Track, but do not virtualize, session handles.
    if (IsSessionHandle(handle)) {
      auto session_handle_iter = session_handles_.find(handle);
      if (session_handle_iter == session_handles_.end()) {
        HandleInfo new_handle_info;
        new_handle_info.Init(handle);
        session_handles_[handle] = new_handle_info;
        VLOG(1) << "OUTPUT_HANDLE_NEW_SESSION: " << std::hex << handle;
      }
      return handle;
    }
    // Only transient object handles are virtualized.
    if (!IsObjectHandle(handle)) {
      return handle;
    }
    auto virtual_handle_iter = tpm_object_handles_.find(handle);
    if (virtual_handle_iter == tpm_object_handles_.end()) {
      TPM_HANDLE new_virtual_handle = CreateVirtualHandle();
      HandleInfo new_handle_info;
      new_handle_info.Init(handle);
      virtual_object_handles_[new_virtual_handle] = new_handle_info;
      tpm_object_handles_[handle] = new_virtual_handle;
      VLOG(1) << "OUTPUT_HANDLE_NEW_VIRTUAL: " << std::hex << handle << " -> "
              << std::hex << new_virtual_handle;
      return new_virtual_handle;
    }
    VLOG(1) << "OUTPUT_HANDLE_REPLACE: " << std::hex << handle << " -> "
            << std::hex << virtual_handle_iter->second;
    return virtual_handle_iter->second;
  }
  
  std::string ResourceManager::ReplaceHandles(
      const std::string& message,
      const std::vector<TPM_HANDLE>& new_handles) {
    std::string handles_blob;
    for (auto handle : new_handles) {
      CHECK_EQ(Serialize_TPM_HANDLE(handle, &handles_blob), TPM_RC_SUCCESS);
    }
    std::string mutable_message = message;
    CHECK_GE(message.size(), kMessageHeaderSize + handles_blob.size());
    return mutable_message.replace(kMessageHeaderSize, handles_blob.size(),
                                   handles_blob);
  }
  
  TPM_RC ResourceManager::SaveContext(const MessageInfo& command_info,
                                      HandleInfo* handle_info) {
    CHECK(handle_info->is_loaded);
    TPM_RC result = TPM_RC_SUCCESS;
    int attempts = 0;
    while (attempts++ < kMaxCommandAttempts) {
      std::string tpm_handle_name;
      Serialize_TPM_HANDLE(handle_info->tpm_handle, &tpm_handle_name);
      result = factory_.GetTpm()->ContextSaveSync(handle_info->tpm_handle,
                                                  tpm_handle_name,
                                                  &handle_info->context,
                                                  nullptr);
      if (!FixWarnings(command_info, result)) {
        break;
      }
    }
    if (result != TPM_RC_SUCCESS) {
      LOG(ERROR) << __func__ << ": Failed to load context: "
                 << GetErrorString(result);
      return result;
    }
    handle_info->is_loaded = false;
    return result;
  }
  
  
  ResourceManager::HandleInfo::HandleInfo()
      : is_loaded(false),
        tpm_handle(0) {
    memset(&context, 0, sizeof(TPMS_CONTEXT));
  }
  
  void ResourceManager::HandleInfo::Init(TPM_HANDLE handle) {
    tpm_handle = handle;
    is_loaded = true;
    time_of_create = base::TimeTicks::Now();
    time_of_last_use = base::TimeTicks::Now();
  }
  
  }  // namespace trunks
  