chromium/chromeos/ash/components/dbus/chaps/chaps_client.cc

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "chromeos/ash/components/dbus/chaps/chaps_client.h"

#include <stdint.h>

#include <type_traits>
#include <utility>
#include <vector>

#include "base/check.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "chromeos/ash/components/dbus/chaps/fake_chaps_client.h"
#include "chromeos/constants/pkcs11_definitions.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "dbus/object_proxy.h"
#include "third_party/cros_system_api/dbus/chaps/dbus-constants.h"

namespace ash {
namespace {
// 5 minutes, since some security element operations can take a while. Should be
// used for computationally heavy operations.
const int kDbusLongTimeoutMillis = base::Minutes(5).InMilliseconds();

// ChromeOS always uses default credentials.
constexpr uint8_t kIsolateCredential[16] = {0, 0, 0, 0, 0, 0, 0, 0,
                                            0, 0, 0, 0, 0, 0, 0, 0};

ChapsClient* g_instance = nullptr;

// Extract an array of uint64_t values from a dbus message associated with the
// `reader`.
bool PopArrayOfUint64s(dbus::MessageReader& reader,
                       std::vector<uint64_t>& array) {
  dbus::MessageReader array_reader(nullptr);
  if (!reader.PopArray(&array_reader)) {
    return false;
  }

  uint64_t value;
  while (array_reader.HasMoreData() && array_reader.PopUint64(&value)) {
    array.emplace_back(value);
  }
  return true;
}

class ChapsClientImpl : public ChapsClient {
 public:
  void Init(dbus::Bus* bus);

  // Implements ChapsClient.
  void GetSlotList(bool token_present, ArrayOfUint64Callback callback) override;
  void GetMechanismList(uint64_t slot_id,
                        ArrayOfUint64Callback callback) override;
  void OpenSession(uint64_t slot_id,
                   uint64_t flags,
                   Uint64Callback callback) override;
  void CloseSession(uint64_t session_id, ResultCodeCallback callback) override;
  void CreateObject(uint64_t session_id,
                    const std::vector<uint8_t>& attributes,
                    Uint64Callback callback) override;
  void DestroyObject(uint64_t session_id,
                     uint64_t object_handle,
                     ResultCodeCallback callback) override;
  void GetAttributeValue(uint64_t session_id,
                         uint64_t object_handle,
                         const std::vector<uint8_t>& attributes_query,
                         GetAttributeValueCallback callback) override;
  void SetAttributeValue(uint64_t session_id,
                         uint64_t object_handle,
                         const std::vector<uint8_t>& attributes,
                         ResultCodeCallback callback) override;
  void FindObjectsInit(uint64_t session_id,
                       const std::vector<uint8_t>& attributes,
                       ResultCodeCallback callback) override;
  void FindObjects(uint64_t session_id,
                   uint64_t max_object_count,
                   ArrayOfUint64Callback callback) override;
  void FindObjectsFinal(uint64_t session_id,
                        ResultCodeCallback callback) override;
  void EncryptInit(uint64_t session_id,
                   uint64_t mechanism_type,
                   const std::vector<uint8_t>& mechanism_parameter,
                   uint64_t key_handle,
                   ResultCodeCallback callback) override;
  void Encrypt(uint64_t session_id,
               const std::vector<uint8_t>& data,
               uint64_t max_out_length,
               DataCallback callback) override;
  void DecryptInit(uint64_t session_id,
                   uint64_t mechanism_type,
                   const std::vector<uint8_t>& mechanism_parameter,
                   uint64_t key_handle,
                   ResultCodeCallback callback) override;
  void Decrypt(uint64_t session_id,
               const std::vector<uint8_t>& data,
               uint64_t max_out_length,
               DataCallback callback) override;
  void SignInit(uint64_t session_id,
                uint64_t mechanism_type,
                const std::vector<uint8_t>& mechanism_parameter,
                uint64_t key_handle,
                ResultCodeCallback callback) override;
  void Sign(uint64_t session_id,
            const std::vector<uint8_t>& data,
            uint64_t max_out_length,
            DataCallback callback) override;
  void GenerateKeyPair(uint64_t session_id,
                       uint64_t mechanism_type,
                       const std::vector<uint8_t>& mechanism_parameter,
                       const std::vector<uint8_t>& public_attributes,
                       const std::vector<uint8_t>& private_attributes,
                       GenerateKeyPairCallback callback) override;
  void WrapKey(uint64_t session_id,
               uint64_t mechanism_type,
               const std::vector<uint8_t>& mechanism_parameter,
               uint64_t wrapping_key_handle,
               uint64_t key_handle,
               uint64_t max_out_length,
               DataCallback callback) override;
  void UnwrapKey(uint64_t session_id,
                 uint64_t mechanism_type,
                 const std::vector<uint8_t>& mechanism_parameter,
                 uint64_t wrapping_key_handle,
                 const std::vector<uint8_t>& wrapped_key,
                 const std::vector<uint8_t>& attributes,
                 Uint64Callback callback) override;
  void DeriveKey(uint64_t session_id,
                 uint64_t mechanism_type,
                 const std::vector<uint8_t>& mechanism_parameter,
                 uint64_t base_key_handle,
                 const std::vector<uint8_t>& attributes,
                 Uint64Callback callback) override;

 private:
  // Handles a response that contains a single result code.
  void ReceiveResultCode(ResultCodeCallback callback, dbus::Response* response);
  // Handles a response that contains a single uint64 (such as an object handle
  // or a session handle) and a result code.
  void ReceiveUint64(Uint64Callback callback, dbus::Response* response);
  // Handles a response that contains an array of uint64 (such as object
  // handles) and a result code.
  void ReceiveArrayOfUint64(ArrayOfUint64Callback callback,
                            dbus::Response* response);
  // Handles a response that contains a uint64 (actual_out_length), an array of
  // bytes (data) and a result code.
  void ReceiveData(DataCallback callback, dbus::Response* response);

  void DidGetAttributeValue(GetAttributeValueCallback callback,
                            dbus::Response* response);
  void DidGenerateKeyPair(GenerateKeyPairCallback callback,
                          dbus::Response* response);

  SEQUENCE_CHECKER(sequence_checker_);

  raw_ptr<dbus::ObjectProxy, LeakedDanglingUntriaged> proxy_ = nullptr;
  base::WeakPtrFactory<ChapsClientImpl> weak_factory_{this};
};

void ChapsClientImpl::Init(dbus::Bus* bus) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  proxy_ = bus->GetObjectProxy(chaps::kChapsServiceName,
                               dbus::ObjectPath(chaps::kChapsServicePath));
  DCHECK(proxy_);
}

void ChapsClientImpl::ReceiveResultCode(ResultCodeCallback callback,
                                        dbus::Response* response) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  if (!response) {
    return std::move(callback).Run(chaps::CKR_DBUS_EMPTY_RESPONSE_ERROR);
  }

  dbus::MessageReader reader(response);
  uint32_t result_code = chromeos::PKCS11_CKR_GENERAL_ERROR;
  if (!reader.PopUint32(&result_code)) {
    return std::move(callback).Run(chaps::CKR_DBUS_DECODING_ERROR);
  }

  return std::move(callback).Run(result_code);
}

void ChapsClientImpl::ReceiveUint64(Uint64Callback callback,
                                    dbus::Response* response) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  if (!response) {
    return std::move(callback).Run(chromeos::PKCS11_INVALID_SESSION_ID,
                                   chaps::CKR_DBUS_EMPTY_RESPONSE_ERROR);
  }

  dbus::MessageReader reader(response);

  uint64_t session_id = 0;
  uint32_t result_code = chromeos::PKCS11_CKR_GENERAL_ERROR;
  // Pop order matters.
  if (!reader.PopUint64(&session_id) || !reader.PopUint32(&result_code)) {
    return std::move(callback).Run(chromeos::PKCS11_INVALID_SESSION_ID,
                                   chaps::CKR_DBUS_DECODING_ERROR);
  }

  return std::move(callback).Run(session_id, result_code);
}

void ChapsClientImpl::ReceiveArrayOfUint64(ArrayOfUint64Callback callback,
                                           dbus::Response* response) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  if (!response) {
    return std::move(callback).Run({}, chaps::CKR_DBUS_EMPTY_RESPONSE_ERROR);
  }

  dbus::MessageReader reader(response);

  std::vector<uint64_t> slot_list;
  uint32_t result_code = chromeos::PKCS11_CKR_GENERAL_ERROR;
  // Pop order matters.
  if (!PopArrayOfUint64s(reader, slot_list) ||
      !reader.PopUint32(&result_code)) {
    return std::move(callback).Run({}, chaps::CKR_DBUS_DECODING_ERROR);
  }

  return std::move(callback).Run(std::move(slot_list), result_code);
}

void ChapsClientImpl::ReceiveData(DataCallback callback,
                                  dbus::Response* response) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  if (!response) {
    return std::move(callback).Run(0, {}, chaps::CKR_DBUS_EMPTY_RESPONSE_ERROR);
  }

  dbus::MessageReader reader(response);
  uint64_t actual_out_length = 0;
  const uint8_t* data_bytes = nullptr;
  size_t data_length = 0;
  uint32_t result_code = chromeos::PKCS11_CKR_GENERAL_ERROR;
  // Pop order matters.
  if (!reader.PopUint64(&actual_out_length) ||
      !reader.PopArrayOfBytes(&data_bytes, &data_length) ||
      !reader.PopUint32(&result_code)) {
    return std::move(callback).Run(0, {}, chaps::CKR_DBUS_DECODING_ERROR);
  };

  return std::move(callback).Run(
      actual_out_length,
      std::vector<uint8_t>(data_bytes, data_bytes + data_length), result_code);
}

void ChapsClientImpl::GetSlotList(bool token_present,
                                  ArrayOfUint64Callback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  dbus::MethodCall method_call(chaps::kChapsInterface,
                               chaps::kGetSlotListMethod);
  dbus::MessageWriter writer(&method_call);
  writer.AppendArrayOfBytes(kIsolateCredential);
  writer.AppendBool(token_present);

  proxy_->CallMethod(
      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
      base::BindOnce(&ChapsClientImpl::ReceiveArrayOfUint64,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void ChapsClientImpl::GetMechanismList(uint64_t slot_id,
                                       ArrayOfUint64Callback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  dbus::MethodCall method_call(chaps::kChapsInterface,
                               chaps::kGetMechanismListMethod);
  dbus::MessageWriter writer(&method_call);
  writer.AppendArrayOfBytes(kIsolateCredential);
  writer.AppendUint64(slot_id);

  proxy_->CallMethod(
      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
      base::BindOnce(&ChapsClientImpl::ReceiveArrayOfUint64,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void ChapsClientImpl::OpenSession(uint64_t slot_id,
                                  uint64_t flags,
                                  Uint64Callback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  dbus::MethodCall method_call(chaps::kChapsInterface,
                               chaps::kOpenSessionMethod);
  dbus::MessageWriter writer(&method_call);
  writer.AppendArrayOfBytes(kIsolateCredential);
  writer.AppendUint64(slot_id);
  writer.AppendUint64(flags);

  proxy_->CallMethod(
      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
      base::BindOnce(&ChapsClientImpl::ReceiveUint64,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void ChapsClientImpl::CloseSession(uint64_t session_id,
                                   ResultCodeCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  dbus::MethodCall method_call(chaps::kChapsInterface,
                               chaps::kCloseSessionMethod);
  dbus::MessageWriter writer(&method_call);
  writer.AppendArrayOfBytes(kIsolateCredential);
  writer.AppendUint64(session_id);

  proxy_->CallMethod(
      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
      base::BindOnce(&ChapsClientImpl::ReceiveResultCode,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void ChapsClientImpl::CreateObject(uint64_t session_id,
                                   const std::vector<uint8_t>& attributes,
                                   Uint64Callback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  dbus::MethodCall method_call(chaps::kChapsInterface,
                               chaps::kCreateObjectMethod);
  dbus::MessageWriter writer(&method_call);
  writer.AppendArrayOfBytes(kIsolateCredential);
  writer.AppendUint64(session_id);
  writer.AppendArrayOfBytes(attributes);

  proxy_->CallMethod(
      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
      base::BindOnce(&ChapsClientImpl::ReceiveUint64,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void ChapsClientImpl::DestroyObject(uint64_t session_id,
                                    uint64_t object_handle,
                                    ResultCodeCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  dbus::MethodCall method_call(chaps::kChapsInterface,
                               chaps::kDestroyObjectMethod);
  dbus::MessageWriter writer(&method_call);
  writer.AppendArrayOfBytes(kIsolateCredential);
  writer.AppendUint64(session_id);
  writer.AppendUint64(object_handle);

  proxy_->CallMethod(
      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
      base::BindOnce(&ChapsClientImpl::ReceiveResultCode,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void ChapsClientImpl::GetAttributeValue(
    uint64_t session_id,
    uint64_t object_handle,
    const std::vector<uint8_t>& attributes_query,
    GetAttributeValueCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  dbus::MethodCall method_call(chaps::kChapsInterface,
                               chaps::kGetAttributeValueMethod);
  dbus::MessageWriter writer(&method_call);
  writer.AppendArrayOfBytes(kIsolateCredential);
  writer.AppendUint64(session_id);
  writer.AppendUint64(object_handle);
  writer.AppendArrayOfBytes(attributes_query);

  proxy_->CallMethod(
      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
      base::BindOnce(&ChapsClientImpl::DidGetAttributeValue,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void ChapsClientImpl::DidGetAttributeValue(GetAttributeValueCallback callback,
                                           dbus::Response* response) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  if (!response) {
    return std::move(callback).Run({}, chaps::CKR_DBUS_EMPTY_RESPONSE_ERROR);
  }

  dbus::MessageReader reader(response);

  const uint8_t* attributes_bytes = nullptr;
  size_t attributes_length = 0;
  uint32_t result_code = chromeos::PKCS11_CKR_GENERAL_ERROR;
  // Pop order matters.
  if (!reader.PopArrayOfBytes(&attributes_bytes, &attributes_length) ||
      !reader.PopUint32(&result_code)) {
    return std::move(callback).Run({}, chaps::CKR_DBUS_DECODING_ERROR);
  }

  return std::move(callback).Run(
      std::vector<uint8_t>(attributes_bytes,
                           attributes_bytes + attributes_length),
      result_code);
}

void ChapsClientImpl::SetAttributeValue(uint64_t session_id,
                                        uint64_t object_handle,
                                        const std::vector<uint8_t>& attributes,
                                        ResultCodeCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  dbus::MethodCall method_call(chaps::kChapsInterface,
                               chaps::kSetAttributeValueMethod);
  dbus::MessageWriter writer(&method_call);
  writer.AppendArrayOfBytes(kIsolateCredential);
  writer.AppendUint64(session_id);
  writer.AppendUint64(object_handle);
  writer.AppendArrayOfBytes(attributes);

  proxy_->CallMethod(
      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
      base::BindOnce(&ChapsClientImpl::ReceiveResultCode,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void ChapsClientImpl::FindObjectsInit(uint64_t session_id,
                                      const std::vector<uint8_t>& attributes,
                                      ResultCodeCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  dbus::MethodCall method_call(chaps::kChapsInterface,
                               chaps::kFindObjectsInitMethod);
  dbus::MessageWriter writer(&method_call);
  writer.AppendArrayOfBytes(kIsolateCredential);
  writer.AppendUint64(session_id);
  writer.AppendArrayOfBytes(attributes);

  proxy_->CallMethod(
      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
      base::BindOnce(&ChapsClientImpl::ReceiveResultCode,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void ChapsClientImpl::FindObjects(uint64_t session_id,
                                  uint64_t max_object_count,
                                  ArrayOfUint64Callback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  dbus::MethodCall method_call(chaps::kChapsInterface,
                               chaps::kFindObjectsMethod);
  dbus::MessageWriter writer(&method_call);
  writer.AppendArrayOfBytes(kIsolateCredential);
  writer.AppendUint64(session_id);
  writer.AppendUint64(max_object_count);

  proxy_->CallMethod(
      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
      base::BindOnce(&ChapsClientImpl::ReceiveArrayOfUint64,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void ChapsClientImpl::FindObjectsFinal(uint64_t session_id,
                                       ResultCodeCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  dbus::MethodCall method_call(chaps::kChapsInterface,
                               chaps::kFindObjectsFinalMethod);
  dbus::MessageWriter writer(&method_call);
  writer.AppendArrayOfBytes(kIsolateCredential);
  writer.AppendUint64(session_id);

  proxy_->CallMethod(
      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
      base::BindOnce(&ChapsClientImpl::ReceiveResultCode,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void ChapsClientImpl::EncryptInit(
    uint64_t session_id,
    uint64_t mechanism_type,
    const std::vector<uint8_t>& mechanism_parameter,
    uint64_t key_handle,
    ResultCodeCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  dbus::MethodCall method_call(chaps::kChapsInterface,
                               chaps::kEncryptInitMethod);
  dbus::MessageWriter writer(&method_call);
  writer.AppendArrayOfBytes(kIsolateCredential);
  writer.AppendUint64(session_id);
  writer.AppendUint64(mechanism_type);
  writer.AppendArrayOfBytes(mechanism_parameter);
  writer.AppendUint64(key_handle);

  proxy_->CallMethod(
      &method_call, kDbusLongTimeoutMillis,
      base::BindOnce(&ChapsClientImpl::ReceiveResultCode,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void ChapsClientImpl::Encrypt(uint64_t session_id,
                              const std::vector<uint8_t>& data,
                              uint64_t max_out_length,
                              DataCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  dbus::MethodCall method_call(chaps::kChapsInterface, chaps::kEncryptMethod);
  dbus::MessageWriter writer(&method_call);
  writer.AppendArrayOfBytes(kIsolateCredential);
  writer.AppendUint64(session_id);
  writer.AppendArrayOfBytes(data);
  writer.AppendUint64(max_out_length);

  proxy_->CallMethod(
      &method_call, kDbusLongTimeoutMillis,
      base::BindOnce(&ChapsClientImpl::ReceiveData, weak_factory_.GetWeakPtr(),
                     std::move(callback)));
}

void ChapsClientImpl::DecryptInit(
    uint64_t session_id,
    uint64_t mechanism_type,
    const std::vector<uint8_t>& mechanism_parameter,
    uint64_t key_handle,
    ResultCodeCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  dbus::MethodCall method_call(chaps::kChapsInterface,
                               chaps::kDecryptInitMethod);
  dbus::MessageWriter writer(&method_call);
  writer.AppendArrayOfBytes(kIsolateCredential);
  writer.AppendUint64(session_id);
  writer.AppendUint64(mechanism_type);
  writer.AppendArrayOfBytes(mechanism_parameter);
  writer.AppendUint64(key_handle);

  proxy_->CallMethod(
      &method_call, kDbusLongTimeoutMillis,
      base::BindOnce(&ChapsClientImpl::ReceiveResultCode,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void ChapsClientImpl::Decrypt(uint64_t session_id,
                              const std::vector<uint8_t>& data,
                              uint64_t max_out_length,
                              DataCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  dbus::MethodCall method_call(chaps::kChapsInterface, chaps::kDecryptMethod);
  dbus::MessageWriter writer(&method_call);
  writer.AppendArrayOfBytes(kIsolateCredential);
  writer.AppendUint64(session_id);
  writer.AppendArrayOfBytes(data);
  writer.AppendUint64(max_out_length);

  proxy_->CallMethod(
      &method_call, kDbusLongTimeoutMillis,
      base::BindOnce(&ChapsClientImpl::ReceiveData, weak_factory_.GetWeakPtr(),
                     std::move(callback)));
}

void ChapsClientImpl::SignInit(uint64_t session_id,
                               uint64_t mechanism_type,
                               const std::vector<uint8_t>& mechanism_parameter,
                               uint64_t key_handle,
                               ResultCodeCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  dbus::MethodCall method_call(chaps::kChapsInterface, chaps::kSignInitMethod);
  dbus::MessageWriter writer(&method_call);
  writer.AppendArrayOfBytes(kIsolateCredential);
  writer.AppendUint64(session_id);
  writer.AppendUint64(mechanism_type);
  writer.AppendArrayOfBytes(mechanism_parameter);
  writer.AppendUint64(key_handle);

  proxy_->CallMethod(
      &method_call, kDbusLongTimeoutMillis,
      base::BindOnce(&ChapsClientImpl::ReceiveResultCode,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void ChapsClientImpl::Sign(uint64_t session_id,
                           const std::vector<uint8_t>& data,
                           uint64_t max_out_length,
                           DataCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  dbus::MethodCall method_call(chaps::kChapsInterface, chaps::kSignMethod);
  dbus::MessageWriter writer(&method_call);
  writer.AppendArrayOfBytes(kIsolateCredential);
  writer.AppendUint64(session_id);
  writer.AppendArrayOfBytes(data);
  writer.AppendUint64(max_out_length);

  proxy_->CallMethod(
      &method_call, kDbusLongTimeoutMillis,
      base::BindOnce(&ChapsClientImpl::ReceiveData, weak_factory_.GetWeakPtr(),
                     std::move(callback)));
}

void ChapsClientImpl::GenerateKeyPair(
    uint64_t session_id,
    uint64_t mechanism_type,
    const std::vector<uint8_t>& mechanism_parameter,
    const std::vector<uint8_t>& public_attributes,
    const std::vector<uint8_t>& private_attributes,
    GenerateKeyPairCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  dbus::MethodCall method_call(chaps::kChapsInterface,
                               chaps::kGenerateKeyPairMethod);
  dbus::MessageWriter writer(&method_call);
  writer.AppendArrayOfBytes(kIsolateCredential);
  writer.AppendUint64(session_id);
  writer.AppendUint64(mechanism_type);
  writer.AppendArrayOfBytes(mechanism_parameter);
  writer.AppendArrayOfBytes(public_attributes);
  writer.AppendArrayOfBytes(private_attributes);

  proxy_->CallMethod(
      &method_call, kDbusLongTimeoutMillis,
      base::BindOnce(&ChapsClientImpl::DidGenerateKeyPair,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void ChapsClientImpl::DidGenerateKeyPair(GenerateKeyPairCallback callback,
                                         dbus::Response* response) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  if (!response) {
    return std::move(callback).Run(uint64_t(0), uint64_t(0),
                                   chaps::CKR_DBUS_EMPTY_RESPONSE_ERROR);
  }

  dbus::MessageReader reader(response);

  uint64_t public_key_id = 0;
  uint64_t private_key_id = 0;
  uint32_t result_code = chromeos::PKCS11_CKR_GENERAL_ERROR;
  // Pop order matters.
  if (!reader.PopUint64(&public_key_id) || !reader.PopUint64(&private_key_id) ||
      !reader.PopUint32(&result_code)) {
    return std::move(callback).Run(uint64_t(0), uint64_t(0),
                                   chaps::CKR_DBUS_DECODING_ERROR);
  }

  return std::move(callback).Run(uint64_t(public_key_id),
                                 uint64_t(private_key_id), result_code);
}

void ChapsClientImpl::WrapKey(uint64_t session_id,
                              uint64_t mechanism_type,
                              const std::vector<uint8_t>& mechanism_parameter,
                              uint64_t wrapping_key_handle,
                              uint64_t key_handle,
                              uint64_t max_out_length,
                              DataCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  dbus::MethodCall method_call(chaps::kChapsInterface, chaps::kWrapKeyMethod);
  dbus::MessageWriter writer(&method_call);
  writer.AppendArrayOfBytes(kIsolateCredential);
  writer.AppendUint64(session_id);
  writer.AppendUint64(mechanism_type);
  writer.AppendArrayOfBytes(mechanism_parameter);
  writer.AppendUint64(wrapping_key_handle);
  writer.AppendUint64(key_handle);
  writer.AppendUint64(max_out_length);

  proxy_->CallMethod(
      &method_call, kDbusLongTimeoutMillis,
      base::BindOnce(&ChapsClientImpl::ReceiveData, weak_factory_.GetWeakPtr(),
                     std::move(callback)));
}

void ChapsClientImpl::UnwrapKey(uint64_t session_id,
                                uint64_t mechanism_type,
                                const std::vector<uint8_t>& mechanism_parameter,
                                uint64_t wrapping_key_handle,
                                const std::vector<uint8_t>& wrapped_key,
                                const std::vector<uint8_t>& attributes,
                                Uint64Callback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  dbus::MethodCall method_call(chaps::kChapsInterface, chaps::kUnwrapKeyMethod);
  dbus::MessageWriter writer(&method_call);
  writer.AppendArrayOfBytes(kIsolateCredential);
  writer.AppendUint64(session_id);
  writer.AppendUint64(mechanism_type);
  writer.AppendArrayOfBytes(mechanism_parameter);
  writer.AppendUint64(wrapping_key_handle);
  writer.AppendArrayOfBytes(wrapped_key);
  writer.AppendArrayOfBytes(attributes);

  proxy_->CallMethod(
      &method_call, kDbusLongTimeoutMillis,
      base::BindOnce(&ChapsClientImpl::ReceiveUint64,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void ChapsClientImpl::DeriveKey(uint64_t session_id,
                                uint64_t mechanism_type,
                                const std::vector<uint8_t>& mechanism_parameter,
                                uint64_t base_key_handle,
                                const std::vector<uint8_t>& attributes,
                                Uint64Callback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  dbus::MethodCall method_call(chaps::kChapsInterface, chaps::kDeriveKeyMethod);
  dbus::MessageWriter writer(&method_call);
  writer.AppendArrayOfBytes(kIsolateCredential);
  writer.AppendUint64(session_id);
  writer.AppendUint64(mechanism_type);
  writer.AppendArrayOfBytes(mechanism_parameter);
  writer.AppendUint64(base_key_handle);
  writer.AppendArrayOfBytes(attributes);

  proxy_->CallMethod(
      &method_call, kDbusLongTimeoutMillis,
      base::BindOnce(&ChapsClientImpl::ReceiveUint64,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

}  // namespace

//   static
ChapsClient* ChapsClient::Get() {
  return g_instance;
}

ChapsClient::ChapsClient() {
  CHECK(!g_instance);
  g_instance = this;
}

ChapsClient::~ChapsClient() {
  CHECK_EQ(this, g_instance);
  g_instance = nullptr;
}

// static
void ChapsClient::Initialize(dbus::Bus* bus) {
  CHECK(bus);
  (new ChapsClientImpl())->Init(bus);
}

// static
void ChapsClient::InitializeFake() {
  new FakeChapsClient();
}

// static
void ChapsClient::Shutdown() {
  CHECK(g_instance);
  delete g_instance;
  // The destructor resets |g_instance|.
  DCHECK(!g_instance);
}

}  // namespace ash