chromium/chromeos/ash/components/dbus/easy_unlock/easy_unlock_client.cc

// Copyright 2014 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/easy_unlock/easy_unlock_client.h"

#include <stddef.h>
#include <stdint.h>

#include <utility>
#include <vector>

#include "base/check.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "chromeos/ash/components/dbus/easy_unlock/fake_easy_unlock_client.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "dbus/object_path.h"
#include "dbus/object_proxy.h"
#include "third_party/cros_system_api/dbus/service_constants.h"

namespace ash {

namespace {

EasyUnlockClient* g_instance = nullptr;

// Reads array of bytes from a dbus message reader and converts it to string.
std::string PopResponseData(dbus::MessageReader* reader) {
  const uint8_t* bytes = NULL;
  size_t length = 0;
  if (!reader->PopArrayOfBytes(&bytes, &length))
    return "";

  return std::string(reinterpret_cast<const char*>(bytes), length);
}

// Converts string to array of bytes and writes it using dbus meddage writer.
void AppendStringAsByteArray(const std::string& data,
                             dbus::MessageWriter* writer) {
  writer->AppendArrayOfBytes(base::as_byte_span(data));
}

// The EasyUnlockClient used in production.
class EasyUnlockClientImpl : public EasyUnlockClient {
 public:
  EasyUnlockClientImpl() : proxy_(nullptr) {}
  EasyUnlockClientImpl(const EasyUnlockClientImpl&) = delete;
  EasyUnlockClientImpl& operator=(const EasyUnlockClientImpl&) = delete;

  ~EasyUnlockClientImpl() override = default;

  // EasyUnlockClient override.
  void GenerateEcP256KeyPair(KeyPairCallback callback) override {
    dbus::MethodCall method_call(easy_unlock::kEasyUnlockServiceInterface,
                                 easy_unlock::kGenerateEcP256KeyPairMethod);
    proxy_->CallMethod(
        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
        base::BindOnce(&EasyUnlockClientImpl::OnKeyPair,
                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
  }

  // EasyUnlockClient override.
  void WrapPublicKey(const std::string& key_algorithm,
                     const std::string& public_key,
                     DataCallback callback) override {
    dbus::MethodCall method_call(easy_unlock::kEasyUnlockServiceInterface,
                                 easy_unlock::kWrapPublicKeyMethod);
    dbus::MessageWriter writer(&method_call);
    writer.AppendString(key_algorithm);
    AppendStringAsByteArray(public_key, &writer);
    proxy_->CallMethod(
        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
        base::BindOnce(&EasyUnlockClientImpl::OnData,
                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
  }

  // EasyUnlockClient override.
  void PerformECDHKeyAgreement(const std::string& private_key,
                               const std::string& public_key,
                               DataCallback callback) override {
    dbus::MethodCall method_call(easy_unlock::kEasyUnlockServiceInterface,
                                 easy_unlock::kPerformECDHKeyAgreementMethod);
    dbus::MessageWriter writer(&method_call);
    // NOTE: DBus expects that data sent as string is UTF-8 encoded. This is
    //     not guaranteed here, so the method uses byte arrays.
    AppendStringAsByteArray(private_key, &writer);
    AppendStringAsByteArray(public_key, &writer);
    proxy_->CallMethod(
        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
        base::BindOnce(&EasyUnlockClientImpl::OnData,
                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
  }

  // EasyUnlockClient override.
  void CreateSecureMessage(const std::string& payload,
                           const CreateSecureMessageOptions& options,
                           DataCallback callback) override {
    dbus::MethodCall method_call(easy_unlock::kEasyUnlockServiceInterface,
                                 easy_unlock::kCreateSecureMessageMethod);
    dbus::MessageWriter writer(&method_call);
    // NOTE: DBus expects that data sent as string is UTF-8 encoded. This is
    //     not guaranteed here, so the method uses byte arrays.
    AppendStringAsByteArray(payload, &writer);
    AppendStringAsByteArray(options.key, &writer);
    AppendStringAsByteArray(options.associated_data, &writer);
    AppendStringAsByteArray(options.public_metadata, &writer);
    AppendStringAsByteArray(options.verification_key_id, &writer);
    AppendStringAsByteArray(options.decryption_key_id, &writer);
    writer.AppendString(options.encryption_type);
    writer.AppendString(options.signature_type);
    proxy_->CallMethod(
        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
        base::BindOnce(&EasyUnlockClientImpl::OnData,
                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
  }

  // EasyUnlockClient override.
  void UnwrapSecureMessage(const std::string& message,
                           const UnwrapSecureMessageOptions& options,
                           DataCallback callback) override {
    dbus::MethodCall method_call(easy_unlock::kEasyUnlockServiceInterface,
                                 easy_unlock::kUnwrapSecureMessageMethod);
    dbus::MessageWriter writer(&method_call);
    // NOTE: DBus expects that data sent as string is UTF-8 encoded. This is
    //     not guaranteed here, so the method uses byte arrays.
    AppendStringAsByteArray(message, &writer);
    AppendStringAsByteArray(options.key, &writer);
    AppendStringAsByteArray(options.associated_data, &writer);
    writer.AppendString(options.encryption_type);
    writer.AppendString(options.signature_type);
    proxy_->CallMethod(
        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
        base::BindOnce(&EasyUnlockClientImpl::OnData,
                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
  }

  void Init(dbus::Bus* bus) override {
    proxy_ = bus->GetObjectProxy(
        easy_unlock::kEasyUnlockServiceName,
        dbus::ObjectPath(easy_unlock::kEasyUnlockServicePath));
  }

 private:
  void OnData(DataCallback callback, dbus::Response* response) {
    if (!response) {
      std::move(callback).Run(std::string());
      return;
    }

    dbus::MessageReader reader(response);
    std::move(callback).Run(PopResponseData(&reader));
  }

  void OnKeyPair(KeyPairCallback callback, dbus::Response* response) {
    if (!response) {
      std::move(callback).Run(std::string(), std::string());
      return;
    }

    dbus::MessageReader reader(response);
    std::string private_key = PopResponseData(&reader);
    std::string public_key = PopResponseData(&reader);

    if (public_key.empty() || private_key.empty()) {
      std::move(callback).Run(std::string(), std::string());
      return;
    }

    std::move(callback).Run(private_key, public_key);
  }

  raw_ptr<dbus::ObjectProxy> proxy_;

  // Note: This should remain the last member so it'll be destroyed and
  // invalidate its weak pointers before any other members are destroyed.
  base::WeakPtrFactory<EasyUnlockClientImpl> weak_ptr_factory_{this};
};

}  // namespace

EasyUnlockClient::CreateSecureMessageOptions::CreateSecureMessageOptions() =
    default;

EasyUnlockClient::CreateSecureMessageOptions::~CreateSecureMessageOptions() =
    default;

EasyUnlockClient::UnwrapSecureMessageOptions::UnwrapSecureMessageOptions() =
    default;

EasyUnlockClient::UnwrapSecureMessageOptions::~UnwrapSecureMessageOptions() =
    default;

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

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

// static
void EasyUnlockClient::InitializeFake() {
  (new FakeEasyUnlockClient())->Init(nullptr);
}

// static
void EasyUnlockClient::Shutdown() {
  CHECK(g_instance);
  delete g_instance;
}

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

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

}  // namespace ash