Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add logic for Device Trust - Key pair
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
Showing
9 changed files
with
374 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
188 changes: 188 additions & 0 deletions
188
chrome/browser/enterprise/connectors/device_trust/device_trust_key_pair.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
75 changes: 75 additions & 0 deletions
75
chrome/browser/enterprise/connectors/device_trust/device_trust_key_pair.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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_ |
68 changes: 68 additions & 0 deletions
68
chrome/browser/enterprise/connectors/device_trust/device_trust_key_pair_unittest.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Oops, something went wrong.