Skip to content

Commit

Permalink
Add and implement mojo interface to generate an uid for EcheApp
Browse files Browse the repository at this point in the history
Use curve25519 to create a key pair and store in PrefServices,
this key pair can be used for digital signatures so that other endpoints
can verify signatures coming from the endpoint that owns the UID.
We use the public key as an UID for now.

Bug: b/180990062
Test: chromeos_components_unittests
Change-Id: I1bc022d45f14fd186f83b056f91e0e975534efbd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2781095
Commit-Queue: Guanru Lee <guanrulee@chromium.org>
Reviewed-by: Nasko Oskov <nasko@chromium.org>
Reviewed-by: David Benjamin <davidben@chromium.org>
Reviewed-by: Dominic Battré <battre@chromium.org>
Reviewed-by: Daniel Nishi <dhnishi@chromium.org>
Reviewed-by: calamity <calamity@chromium.org>
Cr-Commit-Position: refs/heads/master@{#879710}
  • Loading branch information
Guanru Lee authored and Chromium LUCI CQ committed May 6, 2021
1 parent 7397ec9 commit bdca2e6
Show file tree
Hide file tree
Showing 15 changed files with 329 additions and 10 deletions.
4 changes: 4 additions & 0 deletions chrome/browser/chrome_browser_interface_binders.cc
Expand Up @@ -793,6 +793,10 @@ void PopulateChromeWebUIFrameBinders(
chromeos::eche_app::mojom::SystemInfoProvider,
chromeos::eche_app::EcheAppUI>(map);

RegisterWebUIControllerInterfaceBinder<
chromeos::eche_app::mojom::UidGenerator, chromeos::eche_app::EcheAppUI>(
map);

RegisterWebUIControllerInterfaceBinder<
media_app_ui::mojom::PageHandlerFactory, chromeos::MediaAppUI>(map);

Expand Down
14 changes: 11 additions & 3 deletions chrome/browser/chromeos/eche_app/eche_app_manager_factory.cc
Expand Up @@ -20,10 +20,12 @@
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/web_applications/system_web_app_ui_utils.h"
#include "chromeos/components/eche_app_ui/eche_app_manager.h"
#include "chromeos/components/eche_app_ui/eche_uid_provider.h"
#include "chromeos/components/eche_app_ui/system_info.h"
#include "chromeos/components/phonehub/phone_hub_manager.h"
#include "chromeos/strings/grit/chromeos_strings.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/user_manager/user_manager.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/chromeos/devicetype_utils.h"
Expand Down Expand Up @@ -83,6 +85,11 @@ EcheAppManagerFactory::EcheAppManagerFactory()

EcheAppManagerFactory::~EcheAppManagerFactory() = default;

void EcheAppManagerFactory::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterStringPref(kEcheAppSeedPref, "");
}

KeyedService* EcheAppManagerFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
if (!features::IsPhoneHubEnabled() || !features::IsEcheSWAEnabled())
Expand All @@ -108,9 +115,10 @@ KeyedService* EcheAppManagerFactory::BuildServiceInstanceFor(
secure_channel::SecureChannelClientProvider::GetInstance()->GetClient();
if (!secure_channel_client)
return nullptr;
return new EcheAppManager(GetSystemInfo(profile), phone_hub_manager,
device_sync_client, multidevice_setup_client,
secure_channel_client,

return new EcheAppManager(profile->GetPrefs(), GetSystemInfo(profile),
phone_hub_manager, device_sync_client,
multidevice_setup_client, secure_channel_client,
base::BindRepeating(&LaunchEcheApp, profile),
base::BindRepeating(&CloseEcheApp, profile));
}
Expand Down
2 changes: 2 additions & 0 deletions chrome/browser/chromeos/eche_app/eche_app_manager_factory.h
Expand Up @@ -33,6 +33,8 @@ class EcheAppManagerFactory : public BrowserContextKeyedServiceFactory {
// BrowserContextKeyedServiceFactory:
KeyedService* BuildServiceInstanceFor(
content::BrowserContext* context) const override;
void RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) override;

std::unique_ptr<SystemInfo> GetSystemInfo(Profile* profile) const;
};
Expand Down
11 changes: 10 additions & 1 deletion chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
Expand Up @@ -401,6 +401,14 @@ void BindSystemInfoProvider(
}
}

void BindEcheUidGenerator(
chromeos::eche_app::EcheAppManager* manager,
mojo::PendingReceiver<chromeos::eche_app::mojom::UidGenerator> receiver) {
if (manager) {
manager->BindUidGeneratorInterface(std::move(receiver));
}
}

template <>
WebUIController* NewWebUI<chromeos::eche_app::EcheAppUI>(WebUI* web_ui,
const GURL& url) {
Expand All @@ -409,7 +417,8 @@ WebUIController* NewWebUI<chromeos::eche_app::EcheAppUI>(WebUI* web_ui,
chromeos::eche_app::EcheAppManagerFactory::GetForProfile(profile);
return new chromeos::eche_app::EcheAppUI(
web_ui, base::BindRepeating(&BindEcheSignalingMessageExchanger, manager),
base::BindRepeating(&BindSystemInfoProvider, manager));
base::BindRepeating(&BindSystemInfoProvider, manager),
base::BindRepeating(&BindEcheUidGenerator, manager));
}

void BindScanService(
Expand Down
4 changes: 4 additions & 0 deletions chromeos/components/eche_app_ui/BUILD.gn
Expand Up @@ -27,6 +27,8 @@ static_library("eche_app_ui") {
"eche_notification_click_handler.h",
"eche_signaler.cc",
"eche_signaler.h",
"eche_uid_provider.cc",
"eche_uid_provider.h",
"feature_status.cc",
"feature_status.h",
"feature_status_provider.cc",
Expand All @@ -51,6 +53,7 @@ static_library("eche_app_ui") {
"//chromeos/services/device_sync/public/cpp:cpp",
"//chromeos/services/multidevice_setup/public/cpp:cpp",
"//chromeos/services/secure_channel/public/cpp/client:client",
"//components/prefs",
"//content/public/browser",
"//ui/webui",
]
Expand All @@ -74,6 +77,7 @@ source_set("unit_tests") {
sources = [
"eche_feature_status_provider_unittest.cc",
"eche_notification_click_handler_unittest.cc",
"eche_uid_provider_unittest.cc",
"system_info_provider_unittest.cc",
]
deps = [
Expand Down
2 changes: 2 additions & 0 deletions chromeos/components/eche_app_ui/DEPS
Expand Up @@ -2,7 +2,9 @@ include_rules = [
# Do not add chrome here (use a delegate instead).
"+chromeos/grit/chromeos_sample_system_web_app_resources.h",
"+components/keyed_service/core/keyed_service.h",
"+components/prefs",
"+content/public/browser",
"+content/public/common",
"+third_party/boringssl/src/include",
"+ui/webui",
]
11 changes: 10 additions & 1 deletion chromeos/components/eche_app_ui/eche_app_manager.cc
Expand Up @@ -7,6 +7,7 @@
#include "base/system/sys_info.h"
#include "chromeos/components/eche_app_ui/eche_notification_click_handler.h"
#include "chromeos/components/eche_app_ui/eche_signaler.h"
#include "chromeos/components/eche_app_ui/eche_uid_provider.h"
#include "chromeos/components/eche_app_ui/system_info.h"
#include "chromeos/components/eche_app_ui/system_info_provider.h"
#include "chromeos/components/phonehub/notification_interaction_handler.h"
Expand All @@ -23,6 +24,7 @@ const char kMetricNameLatency[] = "Eche.Connectivity.Latency";
namespace eche_app {

EcheAppManager::EcheAppManager(
PrefService* pref_service,
std::unique_ptr<SystemInfo> system_info,
phonehub::PhoneHubManager* phone_hub_manager,
device_sync::DeviceSyncClient* device_sync_client,
Expand Down Expand Up @@ -57,7 +59,8 @@ EcheAppManager::EcheAppManager(
signaler_(std::make_unique<EcheSignaler>(eche_connector_.get(),
connection_manager_.get())),
system_info_provider_(
std::make_unique<SystemInfoProvider>(std::move(system_info))) {}
std::make_unique<SystemInfoProvider>(std::move(system_info))),
uid_(std::make_unique<EcheUidProvider>(pref_service)) {}

EcheAppManager::~EcheAppManager() = default;

Expand All @@ -71,7 +74,13 @@ void EcheAppManager::BindSystemInfoProviderInterface(
system_info_provider_->Bind(std::move(receiver));
}

void EcheAppManager::BindUidGeneratorInterface(
mojo::PendingReceiver<mojom::UidGenerator> receiver) {
uid_->Bind(std::move(receiver));
}

void EcheAppManager::Shutdown() {
uid_.reset();
system_info_provider_.reset();
signaler_.reset();
eche_connector_.reset();
Expand Down
9 changes: 8 additions & 1 deletion chromeos/components/eche_app_ui/eche_app_manager.h
Expand Up @@ -14,6 +14,8 @@
#include "chromeos/components/phonehub/phone_hub_manager.h"
#include "components/keyed_service/core/keyed_service.h"

class PrefService;

namespace chromeos {

namespace device_sync {
Expand All @@ -34,13 +36,15 @@ namespace eche_app {
class SystemInfo;
class EcheSignaler;
class SystemInfoProvider;
class EcheUidProvider;

// Implements the core logic of the EcheApp and exposes interfaces via its
// public API. Implemented as a KeyedService since it depends on other
// KeyedService instances.
class EcheAppManager : public KeyedService {
public:
EcheAppManager(std::unique_ptr<SystemInfo> system_info,
EcheAppManager(PrefService* pref_service,
std::unique_ptr<SystemInfo> system_info,
phonehub::PhoneHubManager*,
device_sync::DeviceSyncClient*,
multidevice_setup::MultiDeviceSetupClient*,
Expand All @@ -54,6 +58,8 @@ class EcheAppManager : public KeyedService {

void BindSignalingMessageExchangerInterface(
mojo::PendingReceiver<mojom::SignalingMessageExchanger> receiver);
void BindUidGeneratorInterface(
mojo::PendingReceiver<mojom::UidGenerator> receiver);

void BindSystemInfoProviderInterface(
mojo::PendingReceiver<mojom::SystemInfoProvider> receiver);
Expand All @@ -69,6 +75,7 @@ class EcheAppManager : public KeyedService {
std::unique_ptr<EcheConnector> eche_connector_;
std::unique_ptr<EcheSignaler> signaler_;
std::unique_ptr<SystemInfoProvider> system_info_provider_;
std::unique_ptr<EcheUidProvider> uid_;
};

} // namespace eche_app
Expand Down
11 changes: 9 additions & 2 deletions chromeos/components/eche_app_ui/eche_app_ui.cc
Expand Up @@ -19,10 +19,12 @@ namespace eche_app {

EcheAppUI::EcheAppUI(content::WebUI* web_ui,
BindSignalingMessageExchangerCallback exchanger_callback,
BindSystemInfoProviderCallback system_info_callback)
BindSystemInfoProviderCallback system_info_callback,
BindUidGeneratorCallback generator_callback)
: ui::MojoWebUIController(web_ui),
bind_exchanger_callback_(std::move(exchanger_callback)),
bind_system_info_callback_(std::move(system_info_callback)) {
bind_system_info_callback_(std::move(system_info_callback)),
bind_generator_callback_(std::move(generator_callback)) {
auto html_source =
base::WrapUnique(content::WebUIDataSource::Create(kChromeUIEcheAppHost));

Expand Down Expand Up @@ -61,6 +63,11 @@ void EcheAppUI::BindInterface(
bind_system_info_callback_.Run(std::move(receiver));
}

void EcheAppUI::BindInterface(
mojo::PendingReceiver<mojom::UidGenerator> receiver) {
bind_generator_callback_.Run(std::move(receiver));
}

WEB_UI_CONTROLLER_TYPE_IMPL(EcheAppUI)

} // namespace eche_app
Expand Down
9 changes: 7 additions & 2 deletions chromeos/components/eche_app_ui/eche_app_ui.h
Expand Up @@ -19,11 +19,13 @@ class EcheAppUI : public ui::MojoWebUIController {
mojo::PendingReceiver<mojom::SignalingMessageExchanger>)>;
using BindSystemInfoProviderCallback = base::RepeatingCallback<void(
mojo::PendingReceiver<mojom::SystemInfoProvider>)>;
using BindUidGeneratorCallback =
base::RepeatingCallback<void(mojo::PendingReceiver<mojom::UidGenerator>)>;

EcheAppUI(content::WebUI* web_ui,
BindSignalingMessageExchangerCallback exchanger_callback,
BindSystemInfoProviderCallback system_info_callback);

BindSystemInfoProviderCallback system_info_callback,
BindUidGeneratorCallback generator_callback);
EcheAppUI(const EcheAppUI&) = delete;
EcheAppUI& operator=(const EcheAppUI&) = delete;
~EcheAppUI() override;
Expand All @@ -33,9 +35,12 @@ class EcheAppUI : public ui::MojoWebUIController {

void BindInterface(mojo::PendingReceiver<mojom::SystemInfoProvider> receiver);

void BindInterface(mojo::PendingReceiver<mojom::UidGenerator> receiver);

private:
const BindSignalingMessageExchangerCallback bind_exchanger_callback_;
const BindSystemInfoProviderCallback bind_system_info_callback_;
const BindUidGeneratorCallback bind_generator_callback_;

WEB_UI_CONTROLLER_TYPE_DECL();
};
Expand Down
104 changes: 104 additions & 0 deletions chromeos/components/eche_app_ui/eche_uid_provider.cc
@@ -0,0 +1,104 @@
// Copyright 2021 The Chromium 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 "chromeos/components/eche_app_ui/eche_uid_provider.h"
#include <base/base64.h>
#include <openssl/base64.h>
#include <cstring>
#include "base/check.h"
#include "chromeos/components/multidevice/logging/logging.h"
#include "components/prefs/pref_service.h"
#include "crypto/random.h"

namespace chromeos {
namespace eche_app {

const char kEcheAppSeedPref[] = "cros.echeapp.seed";
const size_t kSeedSizeInByte = 32;

EcheUidProvider::EcheUidProvider(PrefService* pref_service)
: pref_service_(pref_service) {}

EcheUidProvider::~EcheUidProvider() = default;

void EcheUidProvider::GetUid(
base::OnceCallback<void(const std::string&)> callback) {
if (!uid_.empty()) {
std::move(callback).Run(uid_);
return;
}
uint8_t public_key[ED25519_PUBLIC_KEY_LEN];
uint8_t private_key[ED25519_PRIVATE_KEY_LEN];
std::string pref_seed = pref_service_->GetString(kEcheAppSeedPref);
if (pref_seed.empty()) {
GenerateKeyPair(public_key, private_key);
} else {
base::Optional<std::vector<uint8_t>> result =
ConvertStringToBinary(pref_seed, kSeedSizeInByte);
if (!result) {
PA_LOG(WARNING) << "Invalid encoded string, regenerate the keypair.";
GenerateKeyPair(public_key, private_key);
} else {
DCHECK_EQ(kSeedSizeInByte, result->size());
ED25519_keypair_from_seed(public_key, private_key, result->data());
}
}
uid_ = ConvertBinaryToString(public_key);
std::move(callback).Run(uid_);
}

void EcheUidProvider::GenerateKeyPair(
uint8_t public_key[ED25519_PUBLIC_KEY_LEN],
uint8_t private_key[ED25519_PRIVATE_KEY_LEN]) {
ED25519_keypair(public_key, private_key);
// Store the seed (what RFC8032 calls a private key), which is the
// first 32 bytes of what BoringSSL calls the private key.
pref_service_->SetString(
kEcheAppSeedPref,
ConvertBinaryToString(base::make_span(private_key, kSeedSizeInByte)));
}

base::Optional<std::vector<uint8_t>> EcheUidProvider::ConvertStringToBinary(
base::StringPiece str,
size_t expected_len) {
std::vector<uint8_t> decoded_data(str.size());
size_t decoded_data_len = 0;
if (!EVP_DecodeBase64(
decoded_data.data(), &decoded_data_len, decoded_data.size(),
reinterpret_cast<const uint8_t*>(str.data()), str.size())) {
PA_LOG(ERROR) << "Attempting to decode string failed.";
return base::nullopt;
}
if (decoded_data_len != expected_len) {
PA_LOG(ERROR) << "Expected length is not match.";
return base::nullopt;
}
decoded_data.resize(decoded_data_len);
return decoded_data;
}

std::string EcheUidProvider::ConvertBinaryToString(
base::span<const uint8_t> src) {
// Use a constant-time implementation of base64 in BoringSSL instead of
// base::Base64Encode.
size_t encoded_data_len;
CHECK(EVP_EncodedLength(&encoded_data_len, src.size()) == 1);
std::vector<uint8_t> encoded_data(encoded_data_len);
size_t encoded_block_len =
EVP_EncodeBlock(encoded_data.data(), src.data(), src.size());
// The return value of EVP_EncodeBlock is not the same with the result of
// EVP_EncodedLength, we save the real size of EVP_EncodeBlock to string, not
// the size of buffer.
return std::string(reinterpret_cast<const char*>(encoded_data.data()),
encoded_block_len);
}

void EcheUidProvider::Bind(
mojo::PendingReceiver<mojom::UidGenerator> receiver) {
uid_receiver_.reset();
uid_receiver_.Bind(std::move(receiver));
}

} // namespace eche_app
} // namespace chromeos

0 comments on commit bdca2e6

Please sign in to comment.