Skip to content

Commit

Permalink
Add logic for Device Trust - Key pair
Browse files Browse the repository at this point in the history
Add a key pair generation for Device Trust Connector. This key pair
will be storage in user prefs. For the private key, an ecryptd version
is stored using `OSCrypt`.
Method for exporting public key in PEM format is also added.

Bug: b/172933342
Change-Id: I98baa8f9c64a5dc3ef6f19ff9c068f310c1d8b69
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2648048
Reviewed-by: Roger Tawa <rogerta@chromium.org>
Commit-Queue: Martin Rodriguez <rodmartin@google.com>
Cr-Commit-Position: refs/heads/master@{#858520}
  • Loading branch information
Martin Rodriguez authored and Chromium LUCI CQ committed Mar 1, 2021
1 parent 80c3683 commit 3819f15
Show file tree
Hide file tree
Showing 9 changed files with 374 additions and 5 deletions.
10 changes: 6 additions & 4 deletions chrome/browser/BUILD.gn
Expand Up @@ -484,10 +484,6 @@ static_library("browser") {
"enterprise/browser_management/browser_management_service.h",
"enterprise/browser_management/browser_management_status_provider.cc",
"enterprise/browser_management/browser_management_status_provider.h",
"enterprise/connectors/device_trust/device_trust_factory.cc",
"enterprise/connectors/device_trust/device_trust_factory.h",
"enterprise/connectors/device_trust/device_trust_service.cc",
"enterprise/connectors/device_trust/device_trust_service.h",
"enterprise/util/affiliation.cc",
"enterprise/util/affiliation.h",
"enterprise/util/managed_browser_utils.cc",
Expand Down Expand Up @@ -3572,6 +3568,12 @@ static_library("browser") {
"enterprise/connectors/connectors_prefs.h",
"enterprise/connectors/connectors_service.cc",
"enterprise/connectors/connectors_service.h",
"enterprise/connectors/device_trust/device_trust_factory.cc",
"enterprise/connectors/device_trust/device_trust_factory.h",
"enterprise/connectors/device_trust/device_trust_key_pair.cc",
"enterprise/connectors/device_trust/device_trust_key_pair.h",
"enterprise/connectors/device_trust/device_trust_service.cc",
"enterprise/connectors/device_trust/device_trust_service.h",
"enterprise/connectors/device_trust/signal_reporter.cc",
"enterprise/connectors/device_trust/signal_reporter.h",
"enterprise/connectors/enterprise_connectors_policy_handler.cc",
Expand Down
7 changes: 7 additions & 0 deletions chrome/browser/enterprise/connectors/connectors_prefs.cc
Expand Up @@ -28,6 +28,10 @@ const char kOnSecurityEventPref[] = "enterprise_connectors.on_security_event";

const char kContextAwareAccessSignalsAllowlistPref[] =
"enterprise_connectors.device_trust.origins";
const char kDeviceTrustPrivateKeyPref[] =
"enterprise_connectors.device_trust.private_key";
const char kDeviceTrustPublicKeyPref[] =
"enterprise_connectors.device_trust.public_key";

const char kOnFileAttachedScopePref[] =
"enterprise_connectors.scope.on_file_attached";
Expand Down Expand Up @@ -68,7 +72,10 @@ void RegisterProfilePrefs(PrefRegistrySimple* registry) {
registry->RegisterIntegerPref(kOnFileDownloadedScopePref, 0);
registry->RegisterIntegerPref(kOnBulkDataEntryScopePref, 0);
registry->RegisterIntegerPref(kOnSecurityEventScopePref, 0);
// Device Trust prefs
registry->RegisterListPref(kContextAwareAccessSignalsAllowlistPref);
registry->RegisterStringPref(kDeviceTrustPrivateKeyPref, std::string());
registry->RegisterStringPref(kDeviceTrustPublicKeyPref, std::string());

RegisterFileSystemPrefs(registry);
}
Expand Down
11 changes: 11 additions & 0 deletions chrome/browser/enterprise/connectors/connectors_prefs.h
Expand Up @@ -34,6 +34,17 @@ extern const char kOnFileDownloadedScopePref[];
extern const char kOnBulkDataEntryScopePref[];
extern const char kOnSecurityEventScopePref[];

// The pref name where this class stores the encrypted private key;
// TODO(b/178422353): The attestation key should be per device. If two
// instances of chrome are running, they should report the same key.
// If the machine supports storage in TPM, the private key will be
// stored there.
extern const char kDeviceTrustPrivateKeyPref[];
// The pref name where this class stores the public key;
// If the machine supports storage in TPM, the public key will be
// stored there.
extern const char kDeviceTrustPublicKeyPref[];

void RegisterProfilePrefs(PrefRegistrySimple* registry);

} // namespace enterprise_connectors
Expand Down
@@ -0,0 +1,188 @@
// 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 "chrome/browser/enterprise/connectors/device_trust/device_trust_key_pair.h"

#include "base/base64.h"
#include "base/base64url.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/util/values/values_util.h"
#include "base/values.h"
#include "chrome/browser/enterprise/connectors/connectors_prefs.h"
#include "chrome/common/pref_names.h"
#include "components/os_crypt/os_crypt.h"
#include "components/prefs/pref_service.h"
#include "crypto/ec_signature_creator.h"

namespace policy {

namespace {

const char kBeginPrivateKey[] = "-----BEGIN PRIVATE KEY-----\n";
const char kEndPrivateKey[] = "-----END PRIVATE KEY-----\n";
const char kBeginPublicKey[] = "-----BEGIN PUBLIC KEY-----\n";
const char kEndPublicKey[] = "-----END PUBLIC KEY-----\n";

enum class KeyType { PRIVATE_KEY, PUBLIC_KEY };

std::string BytesToEncodedString(const std::vector<uint8_t>& bytes) {
std::string encoded_string = "";
base::Base64UrlEncode(std::string(bytes.begin(), bytes.end()),
base::Base64UrlEncodePolicy::INCLUDE_PADDING,
&encoded_string);
return encoded_string;
}

std::vector<uint8_t> EncodedStringToBytes(
const base::StringPiece& encoded_string) {
std::string decoded_string;
if (!base::Base64UrlDecode(encoded_string,
base::Base64UrlDecodePolicy::REQUIRE_PADDING,
&decoded_string)) {
return std::vector<uint8_t>();
}
return std::vector<uint8_t>(decoded_string.begin(), decoded_string.end());
}

bool CreatePEMKey(const base::StringPiece& raw_key,
KeyType key_type,
std::string& pem) {
std::string encoded_key;
base::Base64UrlEncode(raw_key, base::Base64UrlEncodePolicy::INCLUDE_PADDING,
&encoded_key);
pem = "";
switch (key_type) {
case KeyType::PRIVATE_KEY:
pem += kBeginPrivateKey;
pem += encoded_key + "\n";
pem += kEndPrivateKey;
break;
case KeyType::PUBLIC_KEY:
pem += kBeginPublicKey;
pem += encoded_key + "\n";
pem += kEndPublicKey;
break;
}
return true;
}

} // namespace

DeviceTrustKeyPair::DeviceTrustKeyPair(Profile* profile) : profile_(profile) {
Init();
}

DeviceTrustKeyPair::~DeviceTrustKeyPair() = default;

bool DeviceTrustKeyPair::Init() {
PrefService* prefs = profile_->GetPrefs();

origins_ = prefs->GetList(
enterprise_connectors::kContextAwareAccessSignalsAllowlistPref);

std::string base64_encrypted_private_key_info =
prefs->GetString(enterprise_connectors::kDeviceTrustPrivateKeyPref);

// No key pair stored.
if (base64_encrypted_private_key_info.empty()) {
key_pair_ = crypto::ECPrivateKey::Create();
return StoreKeyPair();
}

std::string decoded;
if (!base::Base64Decode(base64_encrypted_private_key_info, &decoded)) {
// TODO(crbug/1176175): Handle error for when it can't get the private
// key.
LOG(ERROR) << "[DeviceTrust - Init] error while decoding the private key";
return false;
}

std::string decrypted_private_key_info;
bool success = OSCrypt::DecryptString(decoded, &decrypted_private_key_info);
if (!success) {
// TODO(crbug/1176175): Handle error for when it can't get the private
// key.
LOG(ERROR) << "[DeviceTrust - Init] error while decrypting the private key";
return false;
}

key_pair_ = LoadFromPrivateKeyInfo(decrypted_private_key_info);
if (!key_pair_)
return false;
return true;
}

bool DeviceTrustKeyPair::StoreKeyPair() {
if (!key_pair_) {
LOG(ERROR) << "[DeviceTrust - StoreKeyPair] no `key_pair_` member";
return false;
}

PrefService* prefs = profile_->GetPrefs();
// Add private encoded encrypted private key to prefs.
std::vector<uint8_t> private_key;
if (!key_pair_->ExportPrivateKey(&private_key))
return false;

std::string encoded_private_key = BytesToEncodedString(private_key);
std::string encrypted_key;
bool encrypted = OSCrypt::EncryptString(encoded_private_key, &encrypted_key);
if (!encrypted) {
LOG(ERROR) << "[DeviceTrust - StoreKeyPair] error while encrypting the "
"private key.";
return false;
}
// The string must be encoded as base64 for storage in local state.
std::string encoded;
base::Base64Encode(encrypted_key, &encoded);
prefs->SetString(enterprise_connectors::kDeviceTrustPrivateKeyPref, encoded);

// Add public key to prefs.
prefs->SetString(enterprise_connectors::kDeviceTrustPublicKeyPref,
ExportPEMPublicKey());

return true;
}

std::unique_ptr<crypto::ECPrivateKey>
DeviceTrustKeyPair::LoadFromPrivateKeyInfo(
const std::string& private_key_info_block) {
std::vector<uint8_t> private_key_info =
EncodedStringToBytes(private_key_info_block);
return crypto::ECPrivateKey::CreateFromPrivateKeyInfo(private_key_info);
}

std::string DeviceTrustKeyPair::ExportPEMPrivateKey() {
std::vector<uint8_t> private_key_pkcs8;
key_pair_->ExportPrivateKey(&private_key_pkcs8);
std::string private_key;
if (!CreatePEMKey(BytesToEncodedString(private_key_pkcs8),
KeyType::PRIVATE_KEY, private_key))
return std::string();
return private_key;
}

bool DeviceTrustKeyPair::ExportPublicKey(std::vector<uint8_t>* public_key) {
if (!key_pair_->ExportPublicKey(public_key))
return false;
return true;
}

std::string DeviceTrustKeyPair::ExportPEMPublicKey() {
std::string raw_public_key;
if (!key_pair_->ExportRawPublicKey(&raw_public_key))
return std::string();
std::string public_key;
if (!CreatePEMKey(raw_public_key, KeyType::PUBLIC_KEY, public_key))
return std::string();
return public_key;
}

} // namespace policy
@@ -0,0 +1,75 @@
// 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.

#ifndef CHROME_BROWSER_ENTERPRISE_CONNECTORS_DEVICE_TRUST_DEVICE_TRUST_KEY_PAIR_H_
#define CHROME_BROWSER_ENTERPRISE_CONNECTORS_DEVICE_TRUST_DEVICE_TRUST_KEY_PAIR_H_

#include "chrome/browser/profiles/profile.h"
#include "components/prefs/pref_service.h"
#include "crypto/ec_private_key.h"

namespace policy {

// This class provides functionality used in `DeviceTrustService` class to
// enable Device Trust Connector.
// An instantiation of `DeviceTrustKeyPair` class will be restricted to a single
// instance consisting of a `key_pair_` that is composed of a private key and a
// public key linked to a set of specifics `origins`. This key pair will be
// stored in the user prefs. In the case of the private key, an encrypted
// version is stored using `OSCrypt`.
// `origins` is a list of URLs which indicates the endpoints where the key pair
// will be allowed to interact with.
// The interaction of an endpoint and the key pair a.k.a. handshake will be
// handled by a `NavigationHandle` that will fire this flow when a specific URL
// matches with any of the ones in `origins`. Then the key pair will be
// challenged in the attestation process, generating a challenge-response that
// will be sent back to the endpoint.
//
// Example:
// std::unique_ptr<DeviceTrustKeyPair> key_pair =
// std::make_unique<DeviceTrustKeyPair>(profile, origins);
//
class DeviceTrustKeyPair {
public:
DeviceTrustKeyPair() = delete;
explicit DeviceTrustKeyPair(Profile* profile);
DeviceTrustKeyPair(const DeviceTrustKeyPair&) = delete;
DeviceTrustKeyPair& operator=(const DeviceTrustKeyPair&) = delete;
~DeviceTrustKeyPair();

// Returns a string of the private key in PEM format on success or an empty
// string otherwise.
std::string ExportPEMPrivateKey();

// Returns a string of the public key in PEM format on success or an empty
// string otherwise.
std::string ExportPEMPublicKey();

private:
// TODO(b/181059258) Replace with url_matcher::URLMatcher.
const base::ListValue* origins_;
std::unique_ptr<crypto::ECPrivateKey> key_pair_;
Profile* profile_;

// Load key pair from prefs if available, if not, it will generate a
// new key pair and store the encoded encrypted version of it into prefs.
// Return strue on success.
bool Init();

// Store encrypted private key and public key into prefs.
// Return strue on success.
bool StoreKeyPair();

// Exports the public key to `public_key` as an X.509 SubjectPublicKeyInfo
// block.
bool ExportPublicKey(std::vector<uint8_t>* public_key);

// Load private key from a constant private key info value.
static std::unique_ptr<crypto::ECPrivateKey> LoadFromPrivateKeyInfo(
const std::string& private_key_info_block);
};

} // namespace policy

#endif // CHROME_BROWSER_ENTERPRISE_CONNECTORS_DEVICE_TRUST_DEVICE_TRUST_KEY_PAIR_H_
@@ -0,0 +1,68 @@
// Copyright 2020 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 "chrome/browser/enterprise/connectors/device_trust/device_trust_key_pair.h"

#include "chrome/browser/enterprise/connectors/connectors_prefs.h"
#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
#include "components/os_crypt/os_crypt.h"
#include "components/os_crypt/os_crypt_mocker.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

const base::Value origins[]{base::Value("example1.example.com"),
base::Value("example2.example.com")};

} // namespace

namespace policy {

class DeviceTrustKeyPairTest : public testing::Test {
public:
void SetUp() override {
testing::Test::SetUp();
OSCryptMocker::SetUp();

prefs()->SetUserPref(
enterprise_connectors::kContextAwareAccessSignalsAllowlistPref,
std::make_unique<base::ListValue>(origins));
}

void TearDown() override {
OSCryptMocker::TearDown();
testing::Test::TearDown();
}

TestingPrefServiceSimple* GetTestingLocalState();
sync_preferences::TestingPrefServiceSyncable* prefs() {
return profile_->GetTestingPrefService();
}
TestingProfile* profile() { return profile_.get(); }

private:
content::BrowserTaskEnvironment task_environment_;
std::unique_ptr<TestingProfile> profile_ = std::make_unique<TestingProfile>();
};

TEST_F(DeviceTrustKeyPairTest, ExportPrivateKey) {
policy::DeviceTrustKeyPair key(profile());

std::string private_key = key.ExportPEMPrivateKey();
EXPECT_TRUE(base::StartsWith(private_key, "-----BEGIN PRIVATE KEY-----\n"));
EXPECT_TRUE(base::EndsWith(private_key, "-----END PRIVATE KEY-----\n"));
}

TEST_F(DeviceTrustKeyPairTest, ExportPublicKey) {
policy::DeviceTrustKeyPair key(profile());

std::string public_key = key.ExportPEMPublicKey();
EXPECT_TRUE(base::StartsWith(public_key, "-----BEGIN PUBLIC KEY-----\n"));
EXPECT_TRUE(base::EndsWith(public_key, "-----END PUBLIC KEY-----\n"));
}

} // namespace policy

0 comments on commit 3819f15

Please sign in to comment.