text: // Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "chaps/chaps_service.h"
  
  #include <base/check.h>
  #include <base/logging.h>
  
  #include "chaps/attributes.h"
  #include "chaps/chaps.h"
  #include "chaps/chaps_utility.h"
  #include "chaps/object.h"
  #include "chaps/proto_conversion.h"
  #include "chaps/session.h"
  #include "chaps/slot_manager.h"
  
  using brillo::SecureBlob;
  using std::string;
  using std::vector;
  
  namespace chaps {
  
  ChapsServiceImpl::ChapsServiceImpl(SlotManager* slot_manager)
      : slot_manager_(slot_manager) {
    CHECK(slot_manager);
  }
  
  uint32_t ChapsServiceImpl::GetSlotList(const SecureBlob& isolate_credential,
                                         bool token_present,
                                         vector<uint64_t>* slot_list) {
    if (!slot_list || slot_list->size() > 0)
      LOG_CK_RV_AND_RETURN(CKR_ARGUMENTS_BAD);
    int num_slots = slot_manager_->GetSlotCount();
    for (int i = 0; i < num_slots; ++i) {
      if (slot_manager_->IsTokenAccessible(isolate_credential, i) &&
          (!token_present ||
           slot_manager_->IsTokenPresent(isolate_credential, i)))
        slot_list->push_back(i);
    }
    return CKR_OK;
  }
  
  uint32_t ChapsServiceImpl::GetSlotInfo(const SecureBlob& isolate_credential,
                                         uint64_t slot_id,
                                         SlotInfo* slot_info) {
    LOG_CK_RV_AND_RETURN_IF(!slot_info, CKR_ARGUMENTS_BAD);
    if (static_cast<int>(slot_id) >= slot_manager_->GetSlotCount() ||
        !slot_manager_->IsTokenAccessible(isolate_credential, slot_id))
      LOG_CK_RV_AND_RETURN(CKR_SLOT_ID_INVALID);
    CK_SLOT_INFO ck_slot_info;
    slot_manager_->GetSlotInfo(isolate_credential, slot_id, &ck_slot_info);
    *slot_info = SlotInfoToProto(&ck_slot_info);
    return CKR_OK;
  }
  
  uint32_t ChapsServiceImpl::GetTokenInfo(const SecureBlob& isolate_credential,
                                          uint64_t slot_id,
                                          TokenInfo* token_info) {
    LOG_CK_RV_AND_RETURN_IF(!token_info, CKR_ARGUMENTS_BAD);
    if (static_cast<int>(slot_id) >= slot_manager_->GetSlotCount() ||
        !slot_manager_->IsTokenAccessible(isolate_credential, slot_id))
      LOG_CK_RV_AND_RETURN(CKR_SLOT_ID_INVALID);
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->IsTokenPresent(isolate_credential, slot_id),
        CKR_TOKEN_NOT_PRESENT);
    CK_TOKEN_INFO ck_token_info;
    slot_manager_->GetTokenInfo(isolate_credential, slot_id, &ck_token_info);
    *token_info = TokenInfoToProto(&ck_token_info);
    return CKR_OK;
  }
  
  uint32_t ChapsServiceImpl::GetMechanismList(
      const SecureBlob& isolate_credential,
      uint64_t slot_id,
      vector<uint64_t>* mechanism_list) {
    LOG_CK_RV_AND_RETURN_IF(!mechanism_list || mechanism_list->size() > 0,
                            CKR_ARGUMENTS_BAD);
    if (static_cast<int>(slot_id) >= slot_manager_->GetSlotCount() ||
        !slot_manager_->IsTokenAccessible(isolate_credential, slot_id))
      LOG_CK_RV_AND_RETURN(CKR_SLOT_ID_INVALID);
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->IsTokenPresent(isolate_credential, slot_id),
        CKR_TOKEN_NOT_PRESENT);
    const MechanismMap* mechanism_info =
        slot_manager_->GetMechanismInfo(isolate_credential, slot_id);
    CHECK(mechanism_info);
    for (MechanismMapIterator it = mechanism_info->begin();
         it != mechanism_info->end(); ++it) {
      mechanism_list->push_back(it->first);
    }
    return CKR_OK;
  }
  
  uint32_t ChapsServiceImpl::GetMechanismInfo(
      const SecureBlob& isolate_credential,
      uint64_t slot_id,
      uint64_t mechanism_type,
      MechanismInfo* mechanism_info) {
    LOG_CK_RV_AND_RETURN_IF(!mechanism_info, CKR_ARGUMENTS_BAD);
    if (static_cast<int>(slot_id) >= slot_manager_->GetSlotCount() ||
        !slot_manager_->IsTokenAccessible(isolate_credential, slot_id))
      LOG_CK_RV_AND_RETURN(CKR_SLOT_ID_INVALID);
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->IsTokenPresent(isolate_credential, slot_id),
        CKR_TOKEN_NOT_PRESENT);
    const MechanismMap* mechanism_map =
        slot_manager_->GetMechanismInfo(isolate_credential, slot_id);
    CHECK(mechanism_info);
    MechanismMapIterator it = mechanism_map->find(mechanism_type);
    LOG_CK_RV_AND_RETURN_IF(it == mechanism_map->end(), CKR_MECHANISM_INVALID);
    *mechanism_info = MechanismInfoToProto(&it->second);
    return CKR_OK;
  }
  
  uint32_t ChapsServiceImpl::InitToken(const SecureBlob& isolate_credential,
                                       uint64_t slot_id,
                                       const string* so_pin,
                                       const vector<uint8_t>& label) {
    LOG_CK_RV_AND_RETURN_IF(label.size() != chaps::kTokenLabelSize,
                            CKR_ARGUMENTS_BAD);
    if (static_cast<int>(slot_id) >= slot_manager_->GetSlotCount() ||
        !slot_manager_->IsTokenAccessible(isolate_credential, slot_id))
      LOG_CK_RV_AND_RETURN(CKR_SLOT_ID_INVALID);
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->IsTokenPresent(isolate_credential, slot_id),
        CKR_TOKEN_NOT_PRESENT);
    // We have no notion of a security officer role and no notion of initializing
    // a token via this interface.  CKR_FUNCTION_NOT_SUPPORTED could be an option
    // here but reporting an incorrect pin is more likely to be handled gracefully
    // by the caller.
    LOG_CK_RV_AND_RETURN(CKR_PIN_INCORRECT);
  }
  
  uint32_t ChapsServiceImpl::InitPIN(const SecureBlob& isolate_credential,
                                     uint64_t session_id,
                                     const string* pin) {
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    // Authentication is not handled via this interface.  Since this function can
    // only be called in the "R/W SO Functions" state and we don't support this
    // state, CKR_USER_NOT_LOGGED_IN is the appropriate response.
    LOG_CK_RV_AND_RETURN(CKR_USER_NOT_LOGGED_IN);
  }
  
  uint32_t ChapsServiceImpl::SetPIN(const SecureBlob& isolate_credential,
                                    uint64_t session_id,
                                    const string* old_pin,
                                    const string* new_pin) {
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    // Authentication is not handled via this interface.  We do not support
    // changing a pin or password of any kind.
    LOG_CK_RV_AND_RETURN(CKR_PIN_INVALID);
  }
  
  uint32_t ChapsServiceImpl::OpenSession(const SecureBlob& isolate_credential,
                                         uint64_t slot_id,
                                         uint64_t flags,
                                         uint64_t* session_id) {
    LOG_CK_RV_AND_RETURN_IF(!session_id, CKR_ARGUMENTS_BAD);
    if (static_cast<int>(slot_id) >= slot_manager_->GetSlotCount() ||
        !slot_manager_->IsTokenAccessible(isolate_credential, slot_id))
      LOG_CK_RV_AND_RETURN(CKR_SLOT_ID_INVALID);
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->IsTokenPresent(isolate_credential, slot_id),
        CKR_TOKEN_NOT_PRESENT);
    LOG_CK_RV_AND_RETURN_IF(0 == (flags & CKF_SERIAL_SESSION),
                            CKR_SESSION_PARALLEL_NOT_SUPPORTED);
    *session_id = slot_manager_->OpenSession(isolate_credential, slot_id,
                                             (flags & CKF_RW_SESSION) == 0);
    return CKR_OK;
  }
  
  uint32_t ChapsServiceImpl::CloseSession(const SecureBlob& isolate_credential,
                                          uint64_t session_id) {
    if (!slot_manager_->CloseSession(isolate_credential, session_id))
      LOG_CK_RV_AND_RETURN(CKR_SESSION_HANDLE_INVALID);
    return CKR_OK;
  }
  
  uint32_t ChapsServiceImpl::GetSessionInfo(const SecureBlob& isolate_credential,
                                            uint64_t session_id,
                                            SessionInfo* session_info) {
    LOG_CK_RV_AND_RETURN_IF(!session_info, CKR_ARGUMENTS_BAD);
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    session_info->set_slot_id(static_cast<uint64_t>(session->GetSlot()));
    session_info->set_state(static_cast<uint64_t>(session->GetState()));
    uint64_t flags;
    flags = CKF_SERIAL_SESSION;
    if (!session->IsReadOnly())
      flags |= CKF_RW_SESSION;
    session_info->set_flags(flags);
    session_info->set_device_error(0);
    return CKR_OK;
  }
  
  uint32_t ChapsServiceImpl::GetOperationState(
      const SecureBlob& isolate_credential,
      uint64_t session_id,
      vector<uint8_t>* operation_state) {
    LOG_CK_RV_AND_RETURN_IF(!operation_state, CKR_ARGUMENTS_BAD);
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    LOG_CK_RV_AND_RETURN_IF(!session->IsOperationActive(kEncrypt) &&
                                !session->IsOperationActive(kDecrypt) &&
                                !session->IsOperationActive(kDigest) &&
                                !session->IsOperationActive(kSign) &&
                                !session->IsOperationActive(kVerify),
                            CKR_OPERATION_NOT_INITIALIZED);
    // There is an active operation but we'll still refuse to give out state.
    LOG_CK_RV_AND_RETURN(CKR_STATE_UNSAVEABLE);
  }
  
  uint32_t ChapsServiceImpl::SetOperationState(
      const SecureBlob& isolate_credential,
      uint64_t session_id,
      const vector<uint8_t>& operation_state,
      uint64_t encryption_key_handle,
      uint64_t authentication_key_handle) {
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    // We don't give out operation state so there's no way this is valid.
    LOG_CK_RV_AND_RETURN(CKR_SAVED_STATE_INVALID);
  }
  
  uint32_t ChapsServiceImpl::Login(const SecureBlob& isolate_credential,
                                   uint64_t session_id,
                                   uint64_t user_type,
                                   const string* pin) {
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    // We have no notion of a security officer role.
    LOG_CK_RV_AND_RETURN_IF(user_type == CKU_SO, CKR_PIN_INCORRECT);
    LOG_CK_RV_AND_RETURN_IF(
        user_type != CKU_USER && user_type != CKU_CONTEXT_SPECIFIC,
        CKR_USER_TYPE_INVALID);
    // For backwards compatibility we'll accept the hard-coded pin previously used
    // with openCryptoki.  We'll also accept a protected authentication path
    // operation (i.e. a null pin).
    const string legacy_pin("111111");
    LOG_CK_RV_AND_RETURN_IF(pin && *pin != legacy_pin, CKR_PIN_INCORRECT);
    // After successful C_Login, applications will expect private objects to be
    // available for queries. Don't block waiting for them to become available,
    // return the appropriate error immediately instead.
    LOG_CK_RV_AND_RETURN_IF(!session->IsPrivateLoaded(),
                            CKR_WOULD_BLOCK_FOR_PRIVATE_OBJECTS);
    // We could use CKR_USER_ALREADY_LOGGED_IN but that will cause some
    // applications to close all sessions and start from scratch which is
    // unnecessary.
    return CKR_OK;
  }
  
  uint32_t ChapsServiceImpl::Logout(const SecureBlob& isolate_credential,
                                    uint64_t session_id) {
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    return CKR_OK;
  }
  
  uint32_t ChapsServiceImpl::CreateObject(const SecureBlob& isolate_credential,
                                          uint64_t session_id,
                                          const vector<uint8_t>& attributes,
                                          uint64_t* new_object_handle) {
    LOG_CK_RV_AND_RETURN_IF(!new_object_handle, CKR_ARGUMENTS_BAD);
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    Attributes parsed_attributes;
    LOG_CK_RV_AND_RETURN_IF(!parsed_attributes.Parse(attributes),
                            CKR_TEMPLATE_INCONSISTENT);
    return session->CreateObject(
        parsed_attributes.attributes(), parsed_attributes.num_attributes(),
        PreservedValue<uint64_t, int>(new_object_handle));
  }
  
  uint32_t ChapsServiceImpl::CopyObject(const SecureBlob& isolate_credential,
                                        uint64_t session_id,
                                        uint64_t object_handle,
                                        const vector<uint8_t>& attributes,
                                        uint64_t* new_object_handle) {
    LOG_CK_RV_AND_RETURN_IF(!new_object_handle, CKR_ARGUMENTS_BAD);
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    Attributes parsed_attributes;
    LOG_CK_RV_AND_RETURN_IF(!parsed_attributes.Parse(attributes),
                            CKR_TEMPLATE_INCONSISTENT);
    return session->CopyObject(parsed_attributes.attributes(),
                               parsed_attributes.num_attributes(), object_handle,
                               PreservedValue<uint64_t, int>(new_object_handle));
  }
  
  uint32_t ChapsServiceImpl::DestroyObject(const SecureBlob& isolate_credential,
                                           uint64_t session_id,
                                           uint64_t object_handle) {
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    return session->DestroyObject(object_handle);
  }
  
  uint32_t ChapsServiceImpl::GetObjectSize(const SecureBlob& isolate_credential,
                                           uint64_t session_id,
                                           uint64_t object_handle,
                                           uint64_t* object_size) {
    LOG_CK_RV_AND_RETURN_IF(!object_size, CKR_ARGUMENTS_BAD);
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    const Object* object = NULL;
    LOG_CK_RV_AND_RETURN_IF(!session->GetObject(object_handle, &object),
                            CKR_OBJECT_HANDLE_INVALID);
    CHECK(object);
    *object_size = object->GetSize();
    return CKR_OK;
  }
  
  uint32_t ChapsServiceImpl::GetAttributeValue(
      const SecureBlob& isolate_credential,
      uint64_t session_id,
      uint64_t object_handle,
      const vector<uint8_t>& attributes_in,
      vector<uint8_t>* attributes_out) {
    LOG_CK_RV_AND_RETURN_IF(!attributes_out, CKR_ARGUMENTS_BAD);
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    const Object* object = NULL;
    LOG_CK_RV_AND_RETURN_IF(!session->GetObject(object_handle, &object),
                            CKR_OBJECT_HANDLE_INVALID);
    CHECK(object);
    Attributes tmp;
    LOG_CK_RV_AND_RETURN_IF(!tmp.Parse(attributes_in), CKR_TEMPLATE_INCONSISTENT);
    CK_RV result = object->GetAttributes(tmp.attributes(), tmp.num_attributes());
    if (result == CKR_OK || result == CKR_ATTRIBUTE_SENSITIVE ||
        result == CKR_ATTRIBUTE_TYPE_INVALID || result == CKR_BUFFER_TOO_SMALL) {
      LOG_CK_RV_AND_RETURN_IF(!tmp.Serialize(attributes_out),
                              CKR_FUNCTION_FAILED);
    }
    return result;
  }
  
  uint32_t ChapsServiceImpl::SetAttributeValue(
      const SecureBlob& isolate_credential,
      uint64_t session_id,
      uint64_t object_handle,
      const vector<uint8_t>& attributes) {
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    Object* object = NULL;
    LOG_CK_RV_AND_RETURN_IF(!session->GetModifiableObject(object_handle, &object),
                            CKR_OBJECT_HANDLE_INVALID);
    CHECK(object);
    Attributes tmp;
    LOG_CK_RV_AND_RETURN_IF(!tmp.Parse(attributes), CKR_TEMPLATE_INCONSISTENT);
    CK_RV result = object->SetAttributes(tmp.attributes(), tmp.num_attributes());
    LOG_CK_RV_AND_RETURN_IF_ERR(result);
    result = session->FlushModifiableObject(object);
    LOG_CK_RV_AND_RETURN_IF_ERR(result);
    return CKR_OK;
  }
  
  uint32_t ChapsServiceImpl::FindObjectsInit(const SecureBlob& isolate_credential,
                                             uint64_t session_id,
                                             const vector<uint8_t>& attributes) {
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    Attributes tmp;
    LOG_CK_RV_AND_RETURN_IF(!tmp.Parse(attributes), CKR_TEMPLATE_INCONSISTENT);
    return session->FindObjectsInit(tmp.attributes(), tmp.num_attributes());
  }
  
  uint32_t ChapsServiceImpl::FindObjects(const SecureBlob& isolate_credential,
                                         uint64_t session_id,
                                         uint64_t max_object_count,
                                         vector<uint64_t>* object_list) {
    if (!object_list || object_list->size() > 0)
      LOG_CK_RV_AND_RETURN(CKR_ARGUMENTS_BAD);
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    vector<int> tmp;
    CK_RV result = session->FindObjects(max_object_count, &tmp);
    if (result == CKR_OK) {
      for (size_t i = 0; i < tmp.size(); ++i) {
        object_list->push_back(static_cast<uint64_t>(tmp[i]));
      }
    }
    return result;
  }
  
  uint32_t ChapsServiceImpl::FindObjectsFinal(
      const SecureBlob& isolate_credential, uint64_t session_id) {
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    return session->FindObjectsFinal();
  }
  
  uint32_t ChapsServiceImpl::EncryptInit(
      const SecureBlob& isolate_credential,
      uint64_t session_id,
      uint64_t mechanism_type,
      const vector<uint8_t>& mechanism_parameter,
      uint64_t key_handle) {
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    const Object* key = NULL;
    LOG_CK_RV_AND_RETURN_IF(!session->GetObject(key_handle, &key),
                            CKR_KEY_HANDLE_INVALID);
    CHECK(key);
    return session->OperationInit(kEncrypt, mechanism_type,
                                  ConvertByteVectorToString(mechanism_parameter),
                                  key);
  }
  
  uint32_t ChapsServiceImpl::Encrypt(const SecureBlob& isolate_credential,
                                     uint64_t session_id,
                                     const vector<uint8_t>& data_in,
                                     uint64_t max_out_length,
                                     uint64_t* actual_out_length,
                                     vector<uint8_t>* data_out) {
    LOG_CK_RV_AND_RETURN_IF(!actual_out_length || !data_out, CKR_ARGUMENTS_BAD);
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    *actual_out_length = max_out_length;
    return session->OperationSinglePart(
        kEncrypt, ConvertByteVectorToString(data_in),
        PreservedValue<uint64_t, int>(actual_out_length),
        PreservedByteVector(data_out));
  }
  
  uint32_t ChapsServiceImpl::EncryptUpdate(const SecureBlob& isolate_credential,
                                           uint64_t session_id,
                                           const vector<uint8_t>& data_in,
                                           uint64_t max_out_length,
                                           uint64_t* actual_out_length,
                                           vector<uint8_t>* data_out) {
    LOG_CK_RV_AND_RETURN_IF(!actual_out_length || !data_out, CKR_ARGUMENTS_BAD);
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    *actual_out_length = max_out_length;
    return session->OperationUpdate(
        kEncrypt, ConvertByteVectorToString(data_in),
        PreservedValue<uint64_t, int>(actual_out_length),
        PreservedByteVector(data_out));
  }
  
  uint32_t ChapsServiceImpl::EncryptFinal(const SecureBlob& isolate_credential,
                                          uint64_t session_id,
                                          uint64_t max_out_length,
                                          uint64_t* actual_out_length,
                                          vector<uint8_t>* data_out) {
    LOG_CK_RV_AND_RETURN_IF(!actual_out_length || !data_out, CKR_ARGUMENTS_BAD);
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    *actual_out_length = max_out_length;
    return session->OperationFinal(
        kEncrypt, PreservedValue<uint64_t, int>(actual_out_length),
        PreservedByteVector(data_out));
  }
  
  void ChapsServiceImpl::EncryptCancel(const SecureBlob& isolate_credential,
                                       uint64_t session_id) {
    Session* session = NULL;
    if (!slot_manager_->GetSession(isolate_credential, session_id, &session))
      return;
    CHECK(session);
    session->OperationCancel(kEncrypt);
  }
  
  uint32_t ChapsServiceImpl::DecryptInit(
      const SecureBlob& isolate_credential,
      uint64_t session_id,
      uint64_t mechanism_type,
      const vector<uint8_t>& mechanism_parameter,
      uint64_t key_handle) {
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    const Object* key = NULL;
    LOG_CK_RV_AND_RETURN_IF(!session->GetObject(key_handle, &key),
                            CKR_KEY_HANDLE_INVALID);
    CHECK(key);
    return session->OperationInit(kDecrypt, mechanism_type,
                                  ConvertByteVectorToString(mechanism_parameter),
                                  key);
  }
  
  uint32_t ChapsServiceImpl::Decrypt(const SecureBlob& isolate_credential,
                                     uint64_t session_id,
                                     const vector<uint8_t>& data_in,
                                     uint64_t max_out_length,
                                     uint64_t* actual_out_length,
                                     vector<uint8_t>* data_out) {
    LOG_CK_RV_AND_RETURN_IF(!actual_out_length || !data_out, CKR_ARGUMENTS_BAD);
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    *actual_out_length = max_out_length;
    return session->OperationSinglePart(
        kDecrypt, ConvertByteVectorToString(data_in),
        PreservedValue<uint64_t, int>(actual_out_length),
        PreservedByteVector(data_out));
  }
  
  uint32_t ChapsServiceImpl::DecryptUpdate(const SecureBlob& isolate_credential,
                                           uint64_t session_id,
                                           const vector<uint8_t>& data_in,
                                           uint64_t max_out_length,
                                           uint64_t* actual_out_length,
                                           vector<uint8_t>* data_out) {
    LOG_CK_RV_AND_RETURN_IF(!actual_out_length || !data_out, CKR_ARGUMENTS_BAD);
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    *actual_out_length = max_out_length;
    return session->OperationUpdate(
        kDecrypt, ConvertByteVectorToString(data_in),
        PreservedValue<uint64_t, int>(actual_out_length),
        PreservedByteVector(data_out));
  }
  
  uint32_t ChapsServiceImpl::DecryptFinal(const SecureBlob& isolate_credential,
                                          uint64_t session_id,
                                          uint64_t max_out_length,
                                          uint64_t* actual_out_length,
                                          vector<uint8_t>* data_out) {
    LOG_CK_RV_AND_RETURN_IF(!actual_out_length || !data_out, CKR_ARGUMENTS_BAD);
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    *actual_out_length = max_out_length;
    return session->OperationFinal(
        kDecrypt, PreservedValue<uint64_t, int>(actual_out_length),
        PreservedByteVector(data_out));
  }
  
  void ChapsServiceImpl::DecryptCancel(const SecureBlob& isolate_credential,
                                       uint64_t session_id) {
    Session* session = NULL;
    if (!slot_manager_->GetSession(isolate_credential, session_id, &session))
      return;
    CHECK(session);
    session->OperationCancel(kDecrypt);
  }
  
  uint32_t ChapsServiceImpl::DigestInit(
      const SecureBlob& isolate_credential,
      uint64_t session_id,
      uint64_t mechanism_type,
      const vector<uint8_t>& mechanism_parameter) {
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    return session->OperationInit(kDigest, mechanism_type,
                                  ConvertByteVectorToString(mechanism_parameter),
                                  NULL);
  }
  
  uint32_t ChapsServiceImpl::Digest(const SecureBlob& isolate_credential,
                                    uint64_t session_id,
                                    const vector<uint8_t>& data_in,
                                    uint64_t max_out_length,
                                    uint64_t* actual_out_length,
                                    vector<uint8_t>* digest) {
    LOG_CK_RV_AND_RETURN_IF(!actual_out_length || !digest, CKR_ARGUMENTS_BAD);
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    *actual_out_length = max_out_length;
    return session->OperationSinglePart(
        kDigest, ConvertByteVectorToString(data_in),
        PreservedValue<uint64_t, int>(actual_out_length),
        PreservedByteVector(digest));
  }
  
  uint32_t ChapsServiceImpl::DigestUpdate(const SecureBlob& isolate_credential,
                                          uint64_t session_id,
                                          const vector<uint8_t>& data_in) {
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    return session->OperationUpdate(kDigest, ConvertByteVectorToString(data_in),
                                    NULL, NULL);
  }
  
  uint32_t ChapsServiceImpl::DigestKey(const SecureBlob& isolate_credential,
                                       uint64_t session_id,
                                       uint64_t key_handle) {
    // We don't give out key digests.
    LOG_CK_RV_AND_RETURN(CKR_KEY_INDIGESTIBLE);
  }
  
  uint32_t ChapsServiceImpl::DigestFinal(const SecureBlob& isolate_credential,
                                         uint64_t session_id,
                                         uint64_t max_out_length,
                                         uint64_t* actual_out_length,
                                         vector<uint8_t>* digest) {
    LOG_CK_RV_AND_RETURN_IF(!actual_out_length || !digest, CKR_ARGUMENTS_BAD);
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    *actual_out_length = max_out_length;
    return session->OperationFinal(
        kDigest, PreservedValue<uint64_t, int>(actual_out_length),
        PreservedByteVector(digest));
  }
  
  void ChapsServiceImpl::DigestCancel(const SecureBlob& isolate_credential,
                                      uint64_t session_id) {
    Session* session = NULL;
    if (!slot_manager_->GetSession(isolate_credential, session_id, &session))
      return;
    CHECK(session);
    session->OperationCancel(kDigest);
  }
  
  uint32_t ChapsServiceImpl::SignInit(const SecureBlob& isolate_credential,
                                      uint64_t session_id,
                                      uint64_t mechanism_type,
                                      const vector<uint8_t>& mechanism_parameter,
                                      uint64_t key_handle) {
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    const Object* key = NULL;
    LOG_CK_RV_AND_RETURN_IF(!session->GetObject(key_handle, &key),
                            CKR_KEY_HANDLE_INVALID);
    CHECK(key);
    return session->OperationInit(kSign, mechanism_type,
                                  ConvertByteVectorToString(mechanism_parameter),
                                  key);
  }
  
  uint32_t ChapsServiceImpl::Sign(const SecureBlob& isolate_credential,
                                  uint64_t session_id,
                                  const vector<uint8_t>& data,
                                  uint64_t max_out_length,
                                  uint64_t* actual_out_length,
                                  vector<uint8_t>* signature) {
    LOG_CK_RV_AND_RETURN_IF(!actual_out_length || !signature, CKR_ARGUMENTS_BAD);
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    *actual_out_length = max_out_length;
    return session->OperationSinglePart(
        kSign, ConvertByteVectorToString(data),
        PreservedValue<uint64_t, int>(actual_out_length),
        PreservedByteVector(signature));
  }
  
  uint32_t ChapsServiceImpl::SignUpdate(const SecureBlob& isolate_credential,
                                        uint64_t session_id,
                                        const vector<uint8_t>& data_part) {
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    return session->OperationUpdate(kSign, ConvertByteVectorToString(data_part),
                                    NULL, NULL);
  }
  
  uint32_t ChapsServiceImpl::SignFinal(const SecureBlob& isolate_credential,
                                       uint64_t session_id,
                                       uint64_t max_out_length,
                                       uint64_t* actual_out_length,
                                       vector<uint8_t>* signature) {
    LOG_CK_RV_AND_RETURN_IF(!actual_out_length || !signature, CKR_ARGUMENTS_BAD);
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    *actual_out_length = max_out_length;
    return session->OperationFinal(
        kSign, PreservedValue<uint64_t, int>(actual_out_length),
        PreservedByteVector(signature));
  }
  
  void ChapsServiceImpl::SignCancel(const SecureBlob& isolate_credential,
                                    uint64_t session_id) {
    Session* session = NULL;
    if (!slot_manager_->GetSession(isolate_credential, session_id, &session))
      return;
    CHECK(session);
    session->OperationCancel(kSign);
  }
  
  uint32_t ChapsServiceImpl::SignRecoverInit(
      const SecureBlob& isolate_credential,
      uint64_t session_id,
      uint64_t mechanism_type,
      const vector<uint8_t>& mechanism_parameter,
      uint64_t key_handle) {
    LOG_CK_RV_AND_RETURN(CKR_FUNCTION_NOT_SUPPORTED);
  }
  
  uint32_t ChapsServiceImpl::SignRecover(const SecureBlob& isolate_credential,
                                         uint64_t session_id,
                                         const vector<uint8_t>& data,
                                         uint64_t max_out_length,
                                         uint64_t* actual_out_length,
                                         vector<uint8_t>* signature) {
    LOG_CK_RV_AND_RETURN(CKR_FUNCTION_NOT_SUPPORTED);
  }
  
  uint32_t ChapsServiceImpl::VerifyInit(
      const SecureBlob& isolate_credential,
      uint64_t session_id,
      uint64_t mechanism_type,
      const vector<uint8_t>& mechanism_parameter,
      uint64_t key_handle) {
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    const Object* key = NULL;
    LOG_CK_RV_AND_RETURN_IF(!session->GetObject(key_handle, &key),
                            CKR_KEY_HANDLE_INVALID);
    CHECK(key);
    return session->OperationInit(kVerify, mechanism_type,
                                  ConvertByteVectorToString(mechanism_parameter),
                                  key);
  }
  
  uint32_t ChapsServiceImpl::Verify(const SecureBlob& isolate_credential,
                                    uint64_t session_id,
                                    const vector<uint8_t>& data,
                                    const vector<uint8_t>& signature) {
    CK_RV result = VerifyUpdate(isolate_credential, session_id, data);
    if (result == CKR_OK)
      result = VerifyFinal(isolate_credential, session_id, signature);
    return result;
  }
  
  uint32_t ChapsServiceImpl::VerifyUpdate(const SecureBlob& isolate_credential,
                                          uint64_t session_id,
                                          const vector<uint8_t>& data_part) {
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    return session->OperationUpdate(kVerify, ConvertByteVectorToString(data_part),
                                    NULL, NULL);
  }
  
  uint32_t ChapsServiceImpl::VerifyFinal(const SecureBlob& isolate_credential,
                                         uint64_t session_id,
                                         const vector<uint8_t>& signature) {
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    return session->VerifyFinal(ConvertByteVectorToString(signature));
  }
  
  void ChapsServiceImpl::VerifyCancel(const SecureBlob& isolate_credential,
                                      uint64_t session_id) {
    Session* session = NULL;
    if (!slot_manager_->GetSession(isolate_credential, session_id, &session))
      return;
    CHECK(session);
    session->OperationCancel(kVerify);
  }
  
  uint32_t ChapsServiceImpl::VerifyRecoverInit(
      const SecureBlob& isolate_credential,
      uint64_t session_id,
      uint64_t mechanism_type,
      const vector<uint8_t>& mechanism_parameter,
      uint64_t key_handle) {
    LOG_CK_RV_AND_RETURN(CKR_FUNCTION_NOT_SUPPORTED);
  }
  
  uint32_t ChapsServiceImpl::VerifyRecover(const SecureBlob& isolate_credential,
                                           uint64_t session_id,
                                           const vector<uint8_t>& signature,
                                           uint64_t max_out_length,
                                           uint64_t* actual_out_length,
                                           vector<uint8_t>* data) {
    LOG_CK_RV_AND_RETURN(CKR_FUNCTION_NOT_SUPPORTED);
  }
  
  uint32_t ChapsServiceImpl::DigestEncryptUpdate(
      const SecureBlob& isolate_credential,
      uint64_t session_id,
      const vector<uint8_t>& data_in,
      uint64_t max_out_length,
      uint64_t* actual_out_length,
      vector<uint8_t>* data_out) {
    LOG_CK_RV_AND_RETURN(CKR_FUNCTION_NOT_SUPPORTED);
  }
  
  uint32_t ChapsServiceImpl::DecryptDigestUpdate(
      const SecureBlob& isolate_credential,
      uint64_t session_id,
      const vector<uint8_t>& data_in,
      uint64_t max_out_length,
      uint64_t* actual_out_length,
      vector<uint8_t>* data_out) {
    LOG_CK_RV_AND_RETURN(CKR_FUNCTION_NOT_SUPPORTED);
  }
  
  uint32_t ChapsServiceImpl::SignEncryptUpdate(
      const SecureBlob& isolate_credential,
      uint64_t session_id,
      const vector<uint8_t>& data_in,
      uint64_t max_out_length,
      uint64_t* actual_out_length,
      vector<uint8_t>* data_out) {
    LOG_CK_RV_AND_RETURN(CKR_FUNCTION_NOT_SUPPORTED);
  }
  
  uint32_t ChapsServiceImpl::DecryptVerifyUpdate(
      const SecureBlob& isolate_credential,
      uint64_t session_id,
      const vector<uint8_t>& data_in,
      uint64_t max_out_length,
      uint64_t* actual_out_length,
      vector<uint8_t>* data_out) {
    LOG_CK_RV_AND_RETURN(CKR_FUNCTION_NOT_SUPPORTED);
  }
  
  uint32_t ChapsServiceImpl::GenerateKey(
      const SecureBlob& isolate_credential,
      uint64_t session_id,
      uint64_t mechanism_type,
      const vector<uint8_t>& mechanism_parameter,
      const vector<uint8_t>& attributes,
      uint64_t* key_handle) {
    LOG_CK_RV_AND_RETURN_IF(!key_handle, CKR_ARGUMENTS_BAD);
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    Attributes tmp;
    LOG_CK_RV_AND_RETURN_IF(!tmp.Parse(attributes), CKR_TEMPLATE_INCONSISTENT);
    return session->GenerateKey(mechanism_type,
                                ConvertByteVectorToString(mechanism_parameter),
                                tmp.attributes(), tmp.num_attributes(),
                                PreservedValue<uint64_t, int>(key_handle));
  }
  
  uint32_t ChapsServiceImpl::GenerateKeyPair(
      const SecureBlob& isolate_credential,
      uint64_t session_id,
      uint64_t mechanism_type,
      const vector<uint8_t>& mechanism_parameter,
      const vector<uint8_t>& public_attributes,
      const vector<uint8_t>& private_attributes,
      uint64_t* public_key_handle,
      uint64_t* private_key_handle) {
    LOG_CK_RV_AND_RETURN_IF(!public_key_handle || !private_key_handle,
                            CKR_ARGUMENTS_BAD);
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    Attributes tmp_public;
    LOG_CK_RV_AND_RETURN_IF(!tmp_public.Parse(public_attributes),
                            CKR_TEMPLATE_INCONSISTENT);
    Attributes tmp_private;
    LOG_CK_RV_AND_RETURN_IF(!tmp_private.Parse(private_attributes),
                            CKR_TEMPLATE_INCONSISTENT);
    return session->GenerateKeyPair(
        mechanism_type, ConvertByteVectorToString(mechanism_parameter),
        tmp_public.attributes(), tmp_public.num_attributes(),
        tmp_private.attributes(), tmp_private.num_attributes(),
        PreservedValue<uint64_t, int>(public_key_handle),
        PreservedValue<uint64_t, int>(private_key_handle));
  }
  
  uint32_t ChapsServiceImpl::WrapKey(const SecureBlob& isolate_credential,
                                     uint64_t session_id,
                                     uint64_t mechanism_type,
                                     const vector<uint8_t>& mechanism_parameter,
                                     uint64_t wrapping_key_handle,
                                     uint64_t key_handle,
                                     uint64_t max_out_length,
                                     uint64_t* actual_out_length,
                                     vector<uint8_t>* wrapped_key) {
    LOG_CK_RV_AND_RETURN(CKR_FUNCTION_NOT_SUPPORTED);
  }
  
  uint32_t ChapsServiceImpl::UnwrapKey(const SecureBlob& isolate_credential,
                                       uint64_t session_id,
                                       uint64_t mechanism_type,
                                       const vector<uint8_t>& mechanism_parameter,
                                       uint64_t wrapping_key_handle,
                                       const vector<uint8_t>& wrapped_key,
                                       const vector<uint8_t>& attributes,
                                       uint64_t* key_handle) {
    LOG_CK_RV_AND_RETURN(CKR_FUNCTION_NOT_SUPPORTED);
  }
  
  uint32_t ChapsServiceImpl::DeriveKey(const SecureBlob& isolate_credential,
                                       uint64_t session_id,
                                       uint64_t mechanism_type,
                                       const vector<uint8_t>& mechanism_parameter,
                                       uint64_t base_key_handle,
                                       const vector<uint8_t>& attributes,
                                       uint64_t* key_handle) {
    LOG_CK_RV_AND_RETURN(CKR_FUNCTION_NOT_SUPPORTED);
  }
  
  uint32_t ChapsServiceImpl::SeedRandom(const SecureBlob& isolate_credential,
                                        uint64_t session_id,
                                        const vector<uint8_t>& seed) {
    LOG_CK_RV_AND_RETURN_IF(seed.size() == 0, CKR_ARGUMENTS_BAD);
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    session->SeedRandom(ConvertByteVectorToString(seed));
    return CKR_OK;
  }
  
  uint32_t ChapsServiceImpl::GenerateRandom(const SecureBlob& isolate_credential,
                                            uint64_t session_id,
                                            uint64_t num_bytes,
                                            vector<uint8_t>* random_data) {
    LOG_CK_RV_AND_RETURN_IF(!random_data || num_bytes == 0, CKR_ARGUMENTS_BAD);
    Session* session = NULL;
    LOG_CK_RV_AND_RETURN_IF(
        !slot_manager_->GetSession(isolate_credential, session_id, &session),
        CKR_SESSION_HANDLE_INVALID);
    CHECK(session);
    session->GenerateRandom(num_bytes, PreservedByteVector(random_data));
    return CKR_OK;
  }
  
  }  // namespace chaps
  