chromium/chromeos/ash/components/dbus/chaps/chaps_client_unittest.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.

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

#include <stdint.h>

#include <memory>
#include <utility>
#include <vector>

#include "base/memory/scoped_refptr.h"
#include "base/test/test_future.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "dbus/mock_bus.h"
#include "dbus/mock_object_proxy.h"
#include "dbus/object_path.h"
#include "dbus/object_proxy.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/chaps/dbus-constants.h"

using ::testing::Invoke;
using ::testing::Return;

namespace ash {
namespace {

std::vector<uint8_t> GetIsolateCredential() {
  return std::vector<uint8_t>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
}

std::unique_ptr<dbus::Response> CreateResponse(uint32_t result_code) {
  auto response = dbus::Response::CreateEmpty();
  dbus::MessageWriter writer(response.get());
  writer.AppendUint32(result_code);
  return response;
}

std::unique_ptr<dbus::Response> CreateResponse(uint64_t uint64_value,
                                               uint32_t result_code) {
  auto response = dbus::Response::CreateEmpty();
  dbus::MessageWriter writer(response.get());
  writer.AppendUint64(uint64_value);
  writer.AppendUint32(result_code);
  return response;
}

std::unique_ptr<dbus::Response> CreateResponse(std::vector<uint8_t> bytes,
                                               uint32_t result_code) {
  auto response = dbus::Response::CreateEmpty();
  dbus::MessageWriter writer(response.get());
  writer.AppendArrayOfBytes(bytes);
  writer.AppendUint32(result_code);
  return response;
}

std::unique_ptr<dbus::Response> CreateResponse(std::vector<uint64_t> handles,
                                               uint32_t result_code) {
  auto response = dbus::Response::CreateEmpty();
  dbus::MessageWriter writer(response.get());

  dbus::MessageWriter array_writer(nullptr);
  // See dbus::MessageWriter::OpenArray comment, "t" means uint64_t.
  writer.OpenArray("t", &array_writer);
  for (uint64_t handle : handles) {
    array_writer.AppendUint64(handle);
  }
  writer.CloseContainer(&array_writer);

  writer.AppendUint32(result_code);
  return response;
}

std::unique_ptr<dbus::Response> CreateResponse(uint64_t uint64_value,
                                               std::vector<uint8_t> bytes,
                                               uint32_t result_code) {
  auto response = dbus::Response::CreateEmpty();
  dbus::MessageWriter writer(response.get());
  writer.AppendUint64(uint64_value);
  writer.AppendArrayOfBytes(bytes);
  writer.AppendUint32(result_code);
  return response;
}

std::unique_ptr<dbus::Response> CreateResponse(uint64_t handle0,
                                               uint64_t handle1,
                                               uint32_t result_code) {
  auto response = dbus::Response::CreateEmpty();
  dbus::MessageWriter writer(response.get());
  writer.AppendUint64(handle0);
  writer.AppendUint64(handle1);
  writer.AppendUint32(result_code);
  return response;
}

class SessionChapsClientTest : public testing::Test {
 public:
  void SetUp() override {
    dbus::Bus::Options options;
    options.bus_type = dbus::Bus::SYSTEM;
    bus_ = base::MakeRefCounted<dbus::MockBus>(options);

    std::string chaps_name = "org.chromium.Chaps";
    dbus::ObjectPath chaps_path = dbus::ObjectPath("/org/chromium/Chaps");
    proxy_ = base::MakeRefCounted<dbus::MockObjectProxy>(bus_.get(), chaps_name,
                                                         chaps_path);

    EXPECT_CALL(*bus_.get(), GetObjectProxy(chaps_name, chaps_path))
        .WillRepeatedly(Return(proxy_.get()));

    ChapsClient::Initialize(bus_.get());
    client_ = ChapsClient::Get();
  }

  void TearDown() override {
    client_ = nullptr;
    ChapsClient::Shutdown();
  }

 protected:
  scoped_refptr<dbus::MockBus> bus_;
  scoped_refptr<dbus::MockObjectProxy> proxy_;
  raw_ptr<ChapsClient> client_ = nullptr;
};

bool PopBool(dbus::MessageReader& reader) {
  bool result = false;
  if (!reader.PopBool(&result)) {
    ADD_FAILURE() << "Bad D-Bus request, bool not found";
  }
  return result;
}

uint64_t PopUint64(dbus::MessageReader& reader) {
  uint64_t result = 0;
  if (!reader.PopUint64(&result)) {
    ADD_FAILURE() << "Bad D-Bus request, uint64_t not found";
  }
  return result;
}

std::vector<uint8_t> PopByteArray(dbus::MessageReader& reader) {
  std::vector<uint8_t> result;
  dbus::MessageReader sub_reader(nullptr);
  if (!reader.PopArray(&sub_reader)) {
    ADD_FAILURE() << "Bad D-Bus request, array not found";
    return result;
  }

  uint8_t byte;
  while (sub_reader.HasMoreData()) {
    if (sub_reader.PopByte(&byte)) {
      result.push_back(byte);
    } else {
      ADD_FAILURE() << "Bad D-Bus request, byte not found";
      break;
    }
  }
  return result;
}

// Test that GetSlotList correctly encodes and decodes dbus messages.
TEST_F(SessionChapsClientTest, GetSlotList) {
  bool token_present = true;
  std::vector<uint64_t> out_slots = {2, 3, 4, 5};
  uint32_t result_code = 33;

  auto fake_dbus = [&](dbus::MethodCall* method_call, int timeout_ms,
                       dbus::ObjectProxy::ResponseCallback* callback) {
    EXPECT_EQ(method_call->GetMember(), chaps::kGetSlotListMethod);
    dbus::MessageReader reader(method_call);
    EXPECT_EQ(PopByteArray(reader), GetIsolateCredential());
    EXPECT_EQ(PopBool(reader), token_present);
    EXPECT_FALSE(reader.HasMoreData());

    auto response = CreateResponse(out_slots, result_code);
    return std::move(*callback).Run(response.get());
  };
  EXPECT_CALL(*proxy_.get(), DoCallMethod).WillOnce(Invoke(fake_dbus));

  base::test::TestFuture<const std::vector<uint64_t>&, uint32_t> waiter;
  client_->GetSlotList(token_present, waiter.GetCallback());
  EXPECT_EQ(waiter.Get<std::vector<uint64_t>>(), out_slots);
  EXPECT_EQ(waiter.Get<uint32_t>(), result_code);
}

// Test that GetMechanismList correctly encodes and decodes dbus messages.
TEST_F(SessionChapsClientTest, GetMechanismList) {
  uint64_t slot_id = 11;
  std::vector<uint64_t> out_mechanisms = {2, 3, 4, 5};
  uint32_t result_code = 33;

  auto fake_dbus = [&](dbus::MethodCall* method_call, int timeout_ms,
                       dbus::ObjectProxy::ResponseCallback* callback) {
    EXPECT_EQ(method_call->GetMember(), chaps::kGetMechanismListMethod);
    dbus::MessageReader reader(method_call);
    EXPECT_EQ(PopByteArray(reader), GetIsolateCredential());
    EXPECT_EQ(PopUint64(reader), slot_id);
    EXPECT_FALSE(reader.HasMoreData());

    auto response = CreateResponse(out_mechanisms, result_code);
    return std::move(*callback).Run(response.get());
  };
  EXPECT_CALL(*proxy_.get(), DoCallMethod).WillOnce(Invoke(fake_dbus));

  base::test::TestFuture<const std::vector<uint64_t>&, uint32_t> waiter;
  client_->GetMechanismList(slot_id, waiter.GetCallback());
  EXPECT_EQ(waiter.Get<std::vector<uint64_t>>(), out_mechanisms);
  EXPECT_EQ(waiter.Get<uint32_t>(), result_code);
}

// Test that OpenSession correctly encodes and decodes dbus messages.
TEST_F(SessionChapsClientTest, OpenSession) {
  uint64_t slot_id = 11;
  uint64_t flags = 22;
  uint64_t out_slot_id = 33;
  uint32_t result_code = 44;

  auto fake_dbus = [&](dbus::MethodCall* method_call, int timeout_ms,
                       dbus::ObjectProxy::ResponseCallback* callback) {
    EXPECT_EQ(method_call->GetMember(), chaps::kOpenSessionMethod);
    dbus::MessageReader reader(method_call);
    EXPECT_EQ(PopByteArray(reader), GetIsolateCredential());
    EXPECT_EQ(PopUint64(reader), slot_id);
    EXPECT_EQ(PopUint64(reader), flags);
    EXPECT_FALSE(reader.HasMoreData());

    auto response = CreateResponse(out_slot_id, result_code);
    return std::move(*callback).Run(response.get());
  };
  EXPECT_CALL(*proxy_.get(), DoCallMethod).WillOnce(Invoke(fake_dbus));

  base::test::TestFuture<uint64_t, uint32_t> waiter;
  client_->OpenSession(slot_id, flags, waiter.GetCallback());
  EXPECT_EQ(waiter.Get<uint64_t>(), out_slot_id);
  EXPECT_EQ(waiter.Get<uint32_t>(), result_code);
}

// Test that CloseSession correctly encodes and decodes dbus messages.
TEST_F(SessionChapsClientTest, CloseSession) {
  uint64_t session_id = 11;
  uint32_t result_code = 22;

  auto fake_dbus = [&](dbus::MethodCall* method_call, int timeout_ms,
                       dbus::ObjectProxy::ResponseCallback* callback) {
    EXPECT_EQ(method_call->GetMember(), chaps::kCloseSessionMethod);
    dbus::MessageReader reader(method_call);
    EXPECT_EQ(PopByteArray(reader), GetIsolateCredential());
    EXPECT_EQ(PopUint64(reader), session_id);
    EXPECT_FALSE(reader.HasMoreData());

    auto response = CreateResponse(result_code);
    return std::move(*callback).Run(response.get());
  };
  EXPECT_CALL(*proxy_.get(), DoCallMethod).WillOnce(Invoke(fake_dbus));

  base::test::TestFuture<uint32_t> waiter;
  client_->CloseSession(session_id, waiter.GetCallback());
  EXPECT_EQ(waiter.Get<uint32_t>(), result_code);
}

// Test that CreateObject correctly encodes and decodes dbus messages.
TEST_F(SessionChapsClientTest, CreateObject) {
  uint64_t session_id = 11;
  std::vector<uint8_t> attributes = {2, 2, 2, 2};
  uint64_t out_handle = 33;
  uint32_t result_code = 44;

  auto fake_dbus = [&](dbus::MethodCall* method_call, int timeout_ms,
                       dbus::ObjectProxy::ResponseCallback* callback) {
    EXPECT_EQ(method_call->GetMember(), chaps::kCreateObjectMethod);
    dbus::MessageReader reader(method_call);
    EXPECT_EQ(PopByteArray(reader), GetIsolateCredential());
    EXPECT_EQ(PopUint64(reader), session_id);
    EXPECT_EQ(PopByteArray(reader), attributes);
    EXPECT_FALSE(reader.HasMoreData());

    auto response = CreateResponse(out_handle, result_code);
    return std::move(*callback).Run(response.get());
  };
  EXPECT_CALL(*proxy_.get(), DoCallMethod).WillOnce(Invoke(fake_dbus));

  base::test::TestFuture<uint64_t, uint32_t> waiter;
  client_->CreateObject(session_id, attributes, waiter.GetCallback());
  EXPECT_EQ(waiter.Get<uint64_t>(), out_handle);
  EXPECT_EQ(waiter.Get<uint32_t>(), result_code);
}

// Test that DestroyObject correctly encodes and decodes dbus messages.
TEST_F(SessionChapsClientTest, DestroyObject) {
  uint64_t session_id = 11;
  uint64_t object_handle = 22;
  uint32_t result_code = 33;

  auto fake_dbus = [&](dbus::MethodCall* method_call, int timeout_ms,
                       dbus::ObjectProxy::ResponseCallback* callback) {
    EXPECT_EQ(method_call->GetMember(), chaps::kDestroyObjectMethod);
    dbus::MessageReader reader(method_call);
    EXPECT_EQ(PopByteArray(reader), GetIsolateCredential());
    EXPECT_EQ(PopUint64(reader), session_id);
    EXPECT_EQ(PopUint64(reader), object_handle);
    EXPECT_FALSE(reader.HasMoreData());

    auto response = CreateResponse(result_code);
    return std::move(*callback).Run(response.get());
  };
  EXPECT_CALL(*proxy_.get(), DoCallMethod).WillOnce(Invoke(fake_dbus));

  base::test::TestFuture<uint32_t> waiter;
  client_->DestroyObject(session_id, object_handle, waiter.GetCallback());
  EXPECT_EQ(waiter.Get<uint32_t>(), result_code);
}

// Test that GetAttributeValue correctly encodes and decodes dbus messages.
TEST_F(SessionChapsClientTest, GetAttributeValue) {
  uint64_t session_id = 11;
  uint64_t object_handle = 22;
  std::vector<uint8_t> attributes_query = {3, 3, 3};
  std::vector<uint8_t> out_attributes = {4, 4, 4};
  uint32_t result_code = 55;

  auto fake_dbus = [&](dbus::MethodCall* method_call, int timeout_ms,
                       dbus::ObjectProxy::ResponseCallback* callback) {
    EXPECT_EQ(method_call->GetMember(), chaps::kGetAttributeValueMethod);
    dbus::MessageReader reader(method_call);
    EXPECT_EQ(PopByteArray(reader), GetIsolateCredential());
    EXPECT_EQ(PopUint64(reader), session_id);
    EXPECT_EQ(PopUint64(reader), object_handle);
    EXPECT_EQ(PopByteArray(reader), attributes_query);
    EXPECT_FALSE(reader.HasMoreData());

    auto response = CreateResponse(out_attributes, result_code);
    return std::move(*callback).Run(response.get());
  };
  EXPECT_CALL(*proxy_.get(), DoCallMethod).WillOnce(Invoke(fake_dbus));

  base::test::TestFuture<const std::vector<uint8_t>&, uint32_t> waiter;
  client_->GetAttributeValue(session_id, object_handle, attributes_query,
                             waiter.GetCallback());
  EXPECT_EQ(waiter.Get<std::vector<uint8_t>>(), out_attributes);
  EXPECT_EQ(waiter.Get<uint32_t>(), result_code);
}

// Test that SetAttributeValue correctly encodes and decodes dbus messages.
TEST_F(SessionChapsClientTest, SetAttributeValue) {
  uint64_t session_id = 11;
  uint64_t object_handle = 22;
  std::vector<uint8_t> attributes = {3, 3, 3};
  uint32_t result_code = 44;

  auto fake_dbus = [&](dbus::MethodCall* method_call, int timeout_ms,
                       dbus::ObjectProxy::ResponseCallback* callback) {
    EXPECT_EQ(method_call->GetMember(), chaps::kSetAttributeValueMethod);
    dbus::MessageReader reader(method_call);
    EXPECT_EQ(PopByteArray(reader), GetIsolateCredential());
    EXPECT_EQ(PopUint64(reader), session_id);
    EXPECT_EQ(PopUint64(reader), object_handle);
    EXPECT_EQ(PopByteArray(reader), attributes);
    EXPECT_FALSE(reader.HasMoreData());

    auto response = CreateResponse(result_code);
    return std::move(*callback).Run(response.get());
  };
  EXPECT_CALL(*proxy_.get(), DoCallMethod).WillOnce(Invoke(fake_dbus));

  base::test::TestFuture<uint32_t> waiter;
  client_->SetAttributeValue(session_id, object_handle, attributes,
                             waiter.GetCallback());
  EXPECT_EQ(waiter.Get<uint32_t>(), result_code);
}

// Test that FindObjectsInit correctly encodes and decodes dbus messages.
TEST_F(SessionChapsClientTest, FindObjectsInit) {
  uint64_t session_id = 11;
  std::vector<uint8_t> attributes = {2, 2, 2};
  uint32_t result_code = 33;

  auto fake_dbus = [&](dbus::MethodCall* method_call, int timeout_ms,
                       dbus::ObjectProxy::ResponseCallback* callback) {
    EXPECT_EQ(method_call->GetMember(), chaps::kFindObjectsInitMethod);
    dbus::MessageReader reader(method_call);
    EXPECT_EQ(PopByteArray(reader), GetIsolateCredential());
    EXPECT_EQ(PopUint64(reader), session_id);
    EXPECT_EQ(PopByteArray(reader), attributes);
    EXPECT_FALSE(reader.HasMoreData());

    auto response = CreateResponse(result_code);
    return std::move(*callback).Run(response.get());
  };
  EXPECT_CALL(*proxy_.get(), DoCallMethod).WillOnce(Invoke(fake_dbus));

  base::test::TestFuture<uint32_t> waiter;
  client_->FindObjectsInit(session_id, attributes, waiter.GetCallback());
  EXPECT_EQ(waiter.Get<uint32_t>(), result_code);
}

// Test that FindObjects correctly encodes and decodes dbus messages.
TEST_F(SessionChapsClientTest, FindObjects) {
  uint64_t session_id = 11;
  uint64_t max_object_count = 222222222;
  std::vector<uint64_t> out_handles = {3, 4, 5, 6, 7};
  uint32_t result_code = 44;

  auto fake_dbus = [&](dbus::MethodCall* method_call, int timeout_ms,
                       dbus::ObjectProxy::ResponseCallback* callback) {
    EXPECT_EQ(method_call->GetMember(), chaps::kFindObjectsMethod);
    dbus::MessageReader reader(method_call);
    EXPECT_EQ(PopByteArray(reader), GetIsolateCredential());
    EXPECT_EQ(PopUint64(reader), session_id);
    EXPECT_EQ(PopUint64(reader), max_object_count);
    EXPECT_FALSE(reader.HasMoreData());

    auto response = CreateResponse(out_handles, result_code);
    return std::move(*callback).Run(response.get());
  };
  EXPECT_CALL(*proxy_.get(), DoCallMethod).WillOnce(Invoke(fake_dbus));

  base::test::TestFuture<const std::vector<uint64_t>&, uint32_t> waiter;
  client_->FindObjects(session_id, max_object_count, waiter.GetCallback());
  EXPECT_EQ(waiter.Get<std::vector<uint64_t>>(), out_handles);
  EXPECT_EQ(waiter.Get<uint32_t>(), result_code);
}

// Test that FindObjectsFinal correctly encodes and decodes dbus messages.
TEST_F(SessionChapsClientTest, FindObjectsFinal) {
  uint64_t session_id = 11;
  uint32_t result_code = 22;

  auto fake_dbus = [&](dbus::MethodCall* method_call, int timeout_ms,
                       dbus::ObjectProxy::ResponseCallback* callback) {
    EXPECT_EQ(method_call->GetMember(), chaps::kFindObjectsFinalMethod);
    dbus::MessageReader reader(method_call);
    EXPECT_EQ(PopByteArray(reader), GetIsolateCredential());
    EXPECT_EQ(PopUint64(reader), session_id);
    EXPECT_FALSE(reader.HasMoreData());

    auto response = CreateResponse(result_code);
    return std::move(*callback).Run(response.get());
  };
  EXPECT_CALL(*proxy_.get(), DoCallMethod).WillOnce(Invoke(fake_dbus));

  base::test::TestFuture<uint32_t> waiter;
  client_->FindObjectsFinal(session_id, waiter.GetCallback());
  EXPECT_EQ(waiter.Get<uint32_t>(), result_code);
}

// Test that EncryptInit correctly encodes and decodes dbus messages.
TEST_F(SessionChapsClientTest, EncryptInit) {
  uint64_t session_id = 11;
  uint64_t mechanism_type = 22;
  std::vector<uint8_t> mechanism_parameter = {3, 3, 3};
  uint64_t key_handle = 44;
  uint32_t result_code = 55;

  auto fake_dbus = [&](dbus::MethodCall* method_call, int timeout_ms,
                       dbus::ObjectProxy::ResponseCallback* callback) {
    EXPECT_EQ(method_call->GetMember(), chaps::kEncryptInitMethod);
    dbus::MessageReader reader(method_call);
    EXPECT_EQ(PopByteArray(reader), GetIsolateCredential());
    EXPECT_EQ(PopUint64(reader), session_id);
    EXPECT_EQ(PopUint64(reader), mechanism_type);
    EXPECT_EQ(PopByteArray(reader), mechanism_parameter);
    EXPECT_EQ(PopUint64(reader), key_handle);
    EXPECT_FALSE(reader.HasMoreData());

    auto response = CreateResponse(result_code);
    return std::move(*callback).Run(response.get());
  };
  EXPECT_CALL(*proxy_.get(), DoCallMethod).WillOnce(Invoke(fake_dbus));

  base::test::TestFuture<uint32_t> waiter;
  client_->EncryptInit(session_id, mechanism_type, mechanism_parameter,
                       key_handle, waiter.GetCallback());
  EXPECT_EQ(waiter.Get<uint32_t>(), result_code);
}

// Test that Encrypt correctly encodes and decodes dbus messages.
TEST_F(SessionChapsClientTest, Encrypt) {
  uint64_t session_id = 11;
  std::vector<uint8_t> data = {2, 2, 2};
  uint64_t max_out_length = 33;
  uint64_t actual_out_length = 44;
  std::vector<uint8_t> out_data = {5, 5, 5};
  uint32_t result_code = 66;

  auto fake_dbus = [&](dbus::MethodCall* method_call, int timeout_ms,
                       dbus::ObjectProxy::ResponseCallback* callback) {
    EXPECT_EQ(method_call->GetMember(), chaps::kEncryptMethod);
    dbus::MessageReader reader(method_call);
    EXPECT_EQ(PopByteArray(reader), GetIsolateCredential());
    EXPECT_EQ(PopUint64(reader), session_id);
    EXPECT_EQ(PopByteArray(reader), data);
    EXPECT_EQ(PopUint64(reader), max_out_length);
    EXPECT_FALSE(reader.HasMoreData());

    auto response = CreateResponse(actual_out_length, out_data, result_code);
    return std::move(*callback).Run(response.get());
  };
  EXPECT_CALL(*proxy_.get(), DoCallMethod).WillOnce(Invoke(fake_dbus));

  base::test::TestFuture<uint64_t, const std::vector<uint8_t>&, uint32_t>
      waiter;
  client_->Encrypt(session_id, data, max_out_length, waiter.GetCallback());
  EXPECT_EQ(waiter.Get<uint64_t>(), actual_out_length);
  EXPECT_EQ(waiter.Get<std::vector<uint8_t>>(), out_data);
  EXPECT_EQ(waiter.Get<uint32_t>(), result_code);
}

// Test that DecryptInit correctly encodes and decodes dbus messages.
TEST_F(SessionChapsClientTest, DecryptInit) {
  uint64_t session_id = 11;
  uint64_t mechanism_type = 22;
  std::vector<uint8_t> mechanism_parameter = {3, 3, 3};
  uint64_t key_handle = 44;
  uint32_t result_code = 55;

  auto fake_dbus = [&](dbus::MethodCall* method_call, int timeout_ms,
                       dbus::ObjectProxy::ResponseCallback* callback) {
    EXPECT_EQ(method_call->GetMember(), chaps::kDecryptInitMethod);
    dbus::MessageReader reader(method_call);
    EXPECT_EQ(PopByteArray(reader), GetIsolateCredential());
    EXPECT_EQ(PopUint64(reader), session_id);
    EXPECT_EQ(PopUint64(reader), mechanism_type);
    EXPECT_EQ(PopByteArray(reader), mechanism_parameter);
    EXPECT_EQ(PopUint64(reader), key_handle);
    EXPECT_FALSE(reader.HasMoreData());

    auto response = CreateResponse(result_code);
    return std::move(*callback).Run(response.get());
  };
  EXPECT_CALL(*proxy_.get(), DoCallMethod).WillOnce(Invoke(fake_dbus));

  base::test::TestFuture<uint32_t> waiter;
  client_->DecryptInit(session_id, mechanism_type, mechanism_parameter,
                       key_handle, waiter.GetCallback());
  EXPECT_EQ(waiter.Get<uint32_t>(), result_code);
}

// Test that Decrypt correctly encodes and decodes dbus messages.
TEST_F(SessionChapsClientTest, Decrypt) {
  uint64_t session_id = 11;
  std::vector<uint8_t> data = {2, 2, 2};
  uint64_t max_out_length = 33;
  uint64_t actual_out_length = 44;
  std::vector<uint8_t> out_data = {5, 5, 5};
  uint32_t result_code = 66;

  auto fake_dbus = [&](dbus::MethodCall* method_call, int timeout_ms,
                       dbus::ObjectProxy::ResponseCallback* callback) {
    EXPECT_EQ(method_call->GetMember(), chaps::kDecryptMethod);
    dbus::MessageReader reader(method_call);
    EXPECT_EQ(PopByteArray(reader), GetIsolateCredential());
    EXPECT_EQ(PopUint64(reader), session_id);
    EXPECT_EQ(PopByteArray(reader), data);
    EXPECT_EQ(PopUint64(reader), max_out_length);
    EXPECT_FALSE(reader.HasMoreData());

    auto response = CreateResponse(actual_out_length, out_data, result_code);
    return std::move(*callback).Run(response.get());
  };
  EXPECT_CALL(*proxy_.get(), DoCallMethod).WillOnce(Invoke(fake_dbus));

  base::test::TestFuture<uint64_t, const std::vector<uint8_t>&, uint32_t>
      waiter;
  client_->Decrypt(session_id, data, max_out_length, waiter.GetCallback());
  EXPECT_EQ(waiter.Get<uint64_t>(), actual_out_length);
  EXPECT_EQ(waiter.Get<std::vector<uint8_t>>(), out_data);
  EXPECT_EQ(waiter.Get<uint32_t>(), result_code);
}

// Test that SignInit correctly encodes and decodes dbus messages.
TEST_F(SessionChapsClientTest, SignInit) {
  uint64_t session_id = 11;
  uint64_t mechanism_type = 22;
  std::vector<uint8_t> mechanism_parameter = {3, 3, 3};
  uint64_t key_handle = 44;
  uint32_t result_code = 55;

  auto fake_dbus = [&](dbus::MethodCall* method_call, int timeout_ms,
                       dbus::ObjectProxy::ResponseCallback* callback) {
    EXPECT_EQ(method_call->GetMember(), chaps::kSignInitMethod);
    dbus::MessageReader reader(method_call);
    EXPECT_EQ(PopByteArray(reader), GetIsolateCredential());
    EXPECT_EQ(PopUint64(reader), session_id);
    EXPECT_EQ(PopUint64(reader), mechanism_type);
    EXPECT_EQ(PopByteArray(reader), mechanism_parameter);
    EXPECT_EQ(PopUint64(reader), key_handle);
    EXPECT_FALSE(reader.HasMoreData());

    auto response = CreateResponse(result_code);
    return std::move(*callback).Run(response.get());
  };
  EXPECT_CALL(*proxy_.get(), DoCallMethod).WillOnce(Invoke(fake_dbus));

  base::test::TestFuture<uint32_t> waiter;
  client_->SignInit(session_id, mechanism_type, mechanism_parameter, key_handle,
                    waiter.GetCallback());
  EXPECT_EQ(waiter.Get<uint32_t>(), result_code);
}

// Test that Sign correctly encodes and decodes dbus messages.
TEST_F(SessionChapsClientTest, Sign) {
  uint64_t session_id = 11;
  std::vector<uint8_t> data = {2, 2, 2};
  uint64_t max_out_length = 33;
  uint64_t actual_out_length = 44;
  std::vector<uint8_t> out_data = {5, 5, 5};
  uint32_t result_code = 66;

  auto fake_dbus = [&](dbus::MethodCall* method_call, int timeout_ms,
                       dbus::ObjectProxy::ResponseCallback* callback) {
    EXPECT_EQ(method_call->GetMember(), chaps::kSignMethod);
    dbus::MessageReader reader(method_call);
    EXPECT_EQ(PopByteArray(reader), GetIsolateCredential());
    EXPECT_EQ(PopUint64(reader), session_id);
    EXPECT_EQ(PopByteArray(reader), data);
    EXPECT_EQ(PopUint64(reader), max_out_length);
    EXPECT_FALSE(reader.HasMoreData());

    auto response = CreateResponse(actual_out_length, out_data, result_code);
    return std::move(*callback).Run(response.get());
  };
  EXPECT_CALL(*proxy_.get(), DoCallMethod).WillOnce(Invoke(fake_dbus));

  base::test::TestFuture<uint64_t, const std::vector<uint8_t>&, uint32_t>
      waiter;
  client_->Sign(session_id, data, max_out_length, waiter.GetCallback());
  EXPECT_EQ(waiter.Get<uint64_t>(), actual_out_length);
  EXPECT_EQ(waiter.Get<std::vector<uint8_t>>(), out_data);
  EXPECT_EQ(waiter.Get<uint32_t>(), result_code);
}

// Test that GenerateKeyPair correctly encodes and decodes dbus messages.
TEST_F(SessionChapsClientTest, GenerateKeyPair) {
  uint64_t session_id = 11;
  uint64_t mechanism_type = 22;
  std::vector<uint8_t> mechanism_parameter = {3, 3, 3};
  std::vector<uint8_t> public_attributes = {4, 4, 4};
  std::vector<uint8_t> private_attributes = {5, 5, 5};
  uint64_t public_key_handle = 66;
  uint64_t private_key_handle = 77;
  uint32_t result_code = 88;

  auto fake_dbus = [&](dbus::MethodCall* method_call, int timeout_ms,
                       dbus::ObjectProxy::ResponseCallback* callback) {
    EXPECT_EQ(method_call->GetMember(), chaps::kGenerateKeyPairMethod);
    dbus::MessageReader reader(method_call);
    EXPECT_EQ(PopByteArray(reader), GetIsolateCredential());
    EXPECT_EQ(PopUint64(reader), session_id);
    EXPECT_EQ(PopUint64(reader), mechanism_type);
    EXPECT_EQ(PopByteArray(reader), mechanism_parameter);
    EXPECT_EQ(PopByteArray(reader), public_attributes);
    EXPECT_EQ(PopByteArray(reader), private_attributes);
    EXPECT_FALSE(reader.HasMoreData());

    auto response =
        CreateResponse(public_key_handle, private_key_handle, result_code);
    return std::move(*callback).Run(response.get());
  };
  EXPECT_CALL(*proxy_.get(), DoCallMethod).WillOnce(Invoke(fake_dbus));

  base::test::TestFuture<uint64_t, uint64_t, uint32_t> waiter;
  client_->GenerateKeyPair(session_id, mechanism_type, mechanism_parameter,
                           public_attributes, private_attributes,
                           waiter.GetCallback());
  EXPECT_EQ(waiter.Get<uint32_t>(), result_code);
  EXPECT_EQ(waiter.Get<0>(), public_key_handle);
  EXPECT_EQ(waiter.Get<1>(), private_key_handle);
}

// Test that WrapKey correctly encodes and decodes dbus messages.
TEST_F(SessionChapsClientTest, WrapKey) {
  uint64_t session_id = 11;
  uint64_t mechanism_type = 22;
  std::vector<uint8_t> mechanism_parameter = {3, 3, 3};
  uint64_t wrapping_key_handle = 44;
  uint64_t key_handle = 55;
  uint64_t max_out_length = 66;
  uint64_t actual_out_length = 77;
  std::vector<uint8_t> out_wrapped_key = {8, 8, 8};
  uint32_t result_code = 99;

  auto fake_dbus = [&](dbus::MethodCall* method_call, int timeout_ms,
                       dbus::ObjectProxy::ResponseCallback* callback) {
    EXPECT_EQ(method_call->GetMember(), chaps::kWrapKeyMethod);
    dbus::MessageReader reader(method_call);
    EXPECT_EQ(PopByteArray(reader), GetIsolateCredential());
    EXPECT_EQ(PopUint64(reader), session_id);
    EXPECT_EQ(PopUint64(reader), mechanism_type);
    EXPECT_EQ(PopByteArray(reader), mechanism_parameter);
    EXPECT_EQ(PopUint64(reader), wrapping_key_handle);
    EXPECT_EQ(PopUint64(reader), key_handle);
    EXPECT_EQ(PopUint64(reader), max_out_length);
    EXPECT_FALSE(reader.HasMoreData());

    auto response =
        CreateResponse(actual_out_length, out_wrapped_key, result_code);
    return std::move(*callback).Run(response.get());
  };
  EXPECT_CALL(*proxy_.get(), DoCallMethod).WillOnce(Invoke(fake_dbus));

  base::test::TestFuture<uint64_t, const std::vector<uint8_t>&, uint32_t>
      waiter;
  client_->WrapKey(session_id, mechanism_type, mechanism_parameter,
                   wrapping_key_handle, key_handle, max_out_length,
                   waiter.GetCallback());
  EXPECT_EQ(waiter.Get<uint64_t>(), actual_out_length);
  EXPECT_EQ(waiter.Get<std::vector<uint8_t>>(), out_wrapped_key);
  EXPECT_EQ(waiter.Get<uint32_t>(), result_code);
}

// Test that UnwrapKey correctly encodes and decodes dbus messages.
TEST_F(SessionChapsClientTest, UnwrapKey) {
  uint64_t session_id = 11;
  uint64_t mechanism_type = 22;
  std::vector<uint8_t> mechanism_parameter = {3, 3, 3};
  uint64_t wrapping_key_handle = 44;
  std::vector<uint8_t> wrapped_key = {4, 4, 4};
  std::vector<uint8_t> attributes = {5, 5, 5};
  uint64_t out_key_handle = 77;
  uint32_t result_code = 88;

  auto fake_dbus = [&](dbus::MethodCall* method_call, int timeout_ms,
                       dbus::ObjectProxy::ResponseCallback* callback) {
    EXPECT_EQ(method_call->GetMember(), chaps::kUnwrapKeyMethod);
    dbus::MessageReader reader(method_call);
    EXPECT_EQ(PopByteArray(reader), GetIsolateCredential());
    EXPECT_EQ(PopUint64(reader), session_id);
    EXPECT_EQ(PopUint64(reader), mechanism_type);
    EXPECT_EQ(PopByteArray(reader), mechanism_parameter);
    EXPECT_EQ(PopUint64(reader), wrapping_key_handle);
    EXPECT_EQ(PopByteArray(reader), wrapped_key);
    EXPECT_EQ(PopByteArray(reader), attributes);
    EXPECT_FALSE(reader.HasMoreData());

    auto response = CreateResponse(out_key_handle, result_code);
    return std::move(*callback).Run(response.get());
  };
  EXPECT_CALL(*proxy_.get(), DoCallMethod).WillOnce(Invoke(fake_dbus));

  base::test::TestFuture<uint64_t, uint32_t> waiter;
  client_->UnwrapKey(session_id, mechanism_type, mechanism_parameter,
                     wrapping_key_handle, wrapped_key, attributes,
                     waiter.GetCallback());
  EXPECT_EQ(waiter.Get<uint64_t>(), out_key_handle);
  EXPECT_EQ(waiter.Get<uint32_t>(), result_code);
}

// Test that DeriveKey correctly encodes and decodes dbus messages.
TEST_F(SessionChapsClientTest, DeriveKey) {
  uint64_t session_id = 11;
  uint64_t mechanism_type = 22;
  std::vector<uint8_t> mechanism_parameter = {3, 3, 3};
  uint64_t base_key_handle = 44;
  std::vector<uint8_t> attributes = {5, 5, 5};
  uint64_t out_key_handle = 77;
  uint32_t result_code = 88;

  auto fake_dbus = [&](dbus::MethodCall* method_call, int timeout_ms,
                       dbus::ObjectProxy::ResponseCallback* callback) {
    EXPECT_EQ(method_call->GetMember(), chaps::kDeriveKeyMethod);
    dbus::MessageReader reader(method_call);
    EXPECT_EQ(PopByteArray(reader), GetIsolateCredential());
    EXPECT_EQ(PopUint64(reader), session_id);
    EXPECT_EQ(PopUint64(reader), mechanism_type);
    EXPECT_EQ(PopByteArray(reader), mechanism_parameter);
    EXPECT_EQ(PopUint64(reader), base_key_handle);
    EXPECT_EQ(PopByteArray(reader), attributes);
    EXPECT_FALSE(reader.HasMoreData());

    auto response = CreateResponse(out_key_handle, result_code);
    return std::move(*callback).Run(response.get());
  };
  EXPECT_CALL(*proxy_.get(), DoCallMethod).WillOnce(Invoke(fake_dbus));

  base::test::TestFuture<uint64_t, uint32_t> waiter;
  client_->DeriveKey(session_id, mechanism_type, mechanism_parameter,
                     base_key_handle, attributes, waiter.GetCallback());
  EXPECT_EQ(waiter.Get<uint64_t>(), out_key_handle);
  EXPECT_EQ(waiter.Get<uint32_t>(), result_code);
}

}  // namespace
}  // namespace ash