chromium/chromeos/ash/components/multidevice/secure_message_delegate_impl.cc

// Copyright 2015 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/multidevice/secure_message_delegate_impl.h"

#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "chromeos/ash/components/dbus/easy_unlock/easy_unlock_client.h"
#include "chromeos/ash/components/multidevice/logging/logging.h"
#include "third_party/cros_system_api/dbus/service_constants.h"

namespace ash::multidevice {

namespace {

// Converts encryption type to a string representation used by EasyUnlock dbus
// client.
std::string EncSchemeToString(securemessage::EncScheme scheme) {
  switch (scheme) {
    case securemessage::AES_256_CBC:
      return easy_unlock::kEncryptionTypeAES256CBC;
    case securemessage::NONE:
      return easy_unlock::kEncryptionTypeNone;
  }

  NOTREACHED_IN_MIGRATION();
  return std::string();
}

// Converts signature type to a string representation used by EasyUnlock dbus
// client.
std::string SigSchemeToString(securemessage::SigScheme scheme) {
  switch (scheme) {
    case securemessage::ECDSA_P256_SHA256:
      return easy_unlock::kSignatureTypeECDSAP256SHA256;
    case securemessage::HMAC_SHA256:
      return easy_unlock::kSignatureTypeHMACSHA256;
    case securemessage::RSA2048_SHA256:
      // RSA2048_SHA256 is not supported by the daemon.
      NOTREACHED_IN_MIGRATION();
      return std::string();
  }

  NOTREACHED_IN_MIGRATION();
  return std::string();
}

}  // namespace

// static
SecureMessageDelegateImpl::Factory*
    SecureMessageDelegateImpl::Factory::test_factory_instance_ = nullptr;

// static
std::unique_ptr<SecureMessageDelegate>
SecureMessageDelegateImpl::Factory::Create() {
  if (test_factory_instance_)
    return test_factory_instance_->CreateInstance();

  return base::WrapUnique(new SecureMessageDelegateImpl());
}

// static
void SecureMessageDelegateImpl::Factory::SetFactoryForTesting(
    Factory* test_factory) {
  test_factory_instance_ = test_factory;
}

SecureMessageDelegateImpl::Factory::~Factory() = default;

SecureMessageDelegateImpl::SecureMessageDelegateImpl()
    : dbus_client_(EasyUnlockClient::Get()) {}

SecureMessageDelegateImpl::~SecureMessageDelegateImpl() {}

void SecureMessageDelegateImpl::GenerateKeyPair(
    GenerateKeyPairCallback callback) {
  dbus_client_->GenerateEcP256KeyPair(
      base::BindOnce(&SecureMessageDelegateImpl::OnGenerateKeyPairResult,
                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}

void SecureMessageDelegateImpl::DeriveKey(const std::string& private_key,
                                          const std::string& public_key,
                                          DeriveKeyCallback callback) {
  dbus_client_->PerformECDHKeyAgreement(
      private_key, public_key,
      base::BindOnce(&SecureMessageDelegateImpl::OnDeriveKeyResult,
                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}

void SecureMessageDelegateImpl::CreateSecureMessage(
    const std::string& payload,
    const std::string& key,
    const CreateOptions& create_options,
    CreateSecureMessageCallback callback) {
  if (create_options.signature_scheme == securemessage::RSA2048_SHA256) {
    PA_LOG(ERROR) << "Unable to create message: RSA2048_SHA256 not supported "
                  << "by the ChromeOS daemon.";
    std::move(callback).Run(std::string());
    return;
  }

  EasyUnlockClient::CreateSecureMessageOptions options;
  options.key.assign(key);

  if (!create_options.associated_data.empty())
    options.associated_data.assign(create_options.associated_data);

  if (!create_options.public_metadata.empty())
    options.public_metadata.assign(create_options.public_metadata);

  if (!create_options.verification_key_id.empty())
    options.verification_key_id.assign(create_options.verification_key_id);

  if (!create_options.decryption_key_id.empty())
    options.decryption_key_id.assign(create_options.decryption_key_id);

  options.encryption_type = EncSchemeToString(create_options.encryption_scheme);
  options.signature_type = SigSchemeToString(create_options.signature_scheme);

  dbus_client_->CreateSecureMessage(
      payload, options,
      base::BindOnce(&SecureMessageDelegateImpl::OnCreateSecureMessageResult,
                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}

void SecureMessageDelegateImpl::UnwrapSecureMessage(
    const std::string& serialized_message,
    const std::string& key,
    const UnwrapOptions& unwrap_options,
    UnwrapSecureMessageCallback callback) {
  if (unwrap_options.signature_scheme == securemessage::RSA2048_SHA256) {
    PA_LOG(ERROR) << "Unable to unwrap message: RSA2048_SHA256 not supported "
                  << "by the ChromeOS daemon.";
    std::move(callback).Run(false, std::string(), securemessage::Header());
    return;
  }

  EasyUnlockClient::UnwrapSecureMessageOptions options;
  options.key.assign(key);

  if (!unwrap_options.associated_data.empty())
    options.associated_data.assign(unwrap_options.associated_data);

  options.encryption_type = EncSchemeToString(unwrap_options.encryption_scheme);
  options.signature_type = SigSchemeToString(unwrap_options.signature_scheme);

  dbus_client_->UnwrapSecureMessage(
      serialized_message, options,
      base::BindOnce(&SecureMessageDelegateImpl::OnUnwrapSecureMessageResult,
                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}

void SecureMessageDelegateImpl::OnGenerateKeyPairResult(
    GenerateKeyPairCallback callback,
    const std::string& private_key,
    const std::string& public_key) {
  // The SecureMessageDelegate expects the keys in the reverse order returned by
  // the DBus client.
  std::move(callback).Run(public_key, private_key);
}

void SecureMessageDelegateImpl::OnDeriveKeyResult(
    DeriveKeyCallback callback,
    const std::string& derived_key) {
  std::move(callback).Run(derived_key);
}

void SecureMessageDelegateImpl::OnCreateSecureMessageResult(
    CreateSecureMessageCallback callback,
    const std::string& secure_message) {
  std::move(callback).Run(secure_message);
}

void SecureMessageDelegateImpl::OnUnwrapSecureMessageResult(
    UnwrapSecureMessageCallback callback,
    const std::string& unwrap_result) {
  securemessage::HeaderAndBody header_and_body;
  if (!header_and_body.ParseFromString(unwrap_result)) {
    std::move(callback).Run(false, std::string(), securemessage::Header());
  } else {
    std::move(callback).Run(true, header_and_body.body(),
                            header_and_body.header());
  }
}

}  // namespace ash::multidevice