Commit 4b6040f2 authored by John Budorick's avatar John Budorick Committed by Commit Bot

Revert "Add CertProvisioningWorker class"

This reverts commit d8c37dee.

Reason for revert: spec revert for https://bugs.chromium.org/p/chromium/issues/detail?id=1062423

Original change's description:
> Add CertProvisioningWorker class
> 
> CertProvisioningWorker class implements core logic for building
> a CSR with DM server and importing corresponding certificate.
> 
> Bug: 1045895
> Test: CertProvisioningWorkerTest.*
> Change-Id: Ida540f2b330d0dac9e86f53568f29287559a1ad8
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2078582
> Commit-Queue: Michael Ershov <miersh@google.com>
> Reviewed-by: Pavol Marko <pmarko@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#750986}

TBR=achuith@chromium.org,pmarko@chromium.org,miersh@google.com

Change-Id: I06d5c2889f05563980a20b42ff00c635da5677db
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 1045895
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2107394Reviewed-by: default avatarJohn Budorick <jbudorick@chromium.org>
Commit-Queue: John Budorick <jbudorick@chromium.org>
Cr-Commit-Position: refs/heads/master@{#751138}
parent d004d912
......@@ -781,8 +781,6 @@ source_set("chromeos") {
"camera_presence_notifier.h",
"cert_provisioning/cert_provisioning_common.cc",
"cert_provisioning/cert_provisioning_common.h",
"cert_provisioning/cert_provisioning_worker.cc",
"cert_provisioning/cert_provisioning_worker.h",
"certificate_provider/certificate_info.cc",
"certificate_provider/certificate_info.h",
"certificate_provider/certificate_provider.h",
......@@ -2757,7 +2755,6 @@ source_set("unit_tests") {
"authpolicy/authpolicy_helper.unittest.cc",
"base/file_flusher_unittest.cc",
"bluetooth/debug_logs_manager_unittest.cc",
"cert_provisioning/cert_provisioning_worker_unittest.cc",
"certificate_provider/certificate_provider_service_unittest.cc",
"child_accounts/child_user_service_unittest.cc",
"child_accounts/event_based_status_reporting_service_unittest.cc",
......
......@@ -4,7 +4,6 @@
#include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h"
#include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_registry_simple.h"
......@@ -19,45 +18,5 @@ void RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
registry->RegisterListPref(prefs::kRequiredClientCertificateForDevice);
}
std::string GetKeyName(CertProfileId profile_id) {
return kKeyNamePrefix + profile_id;
}
attestation::AttestationKeyType GetVaKeyType(CertScope scope) {
switch (scope) {
case CertScope::kUser:
return attestation::AttestationKeyType::KEY_USER;
case CertScope::kDevice:
return attestation::AttestationKeyType::KEY_DEVICE;
}
}
std::string GetVaKeyName(CertScope scope, CertProfileId profile_id) {
switch (scope) {
case CertScope::kUser:
return GetKeyName(profile_id);
case CertScope::kDevice:
return std::string();
}
}
std::string GetVaKeyNameForSpkac(CertScope scope, CertProfileId profile_id) {
switch (scope) {
case CertScope::kUser:
return std::string();
case CertScope::kDevice:
return GetKeyName(profile_id);
}
}
const char* GetPlatformKeysTokenId(CertScope scope) {
switch (scope) {
case CertScope::kUser:
return platform_keys::kTokenIdUser;
case CertScope::kDevice:
return platform_keys::kTokenIdSystem;
}
}
} // namespace cert_provisioning
} // namespace chromeos
......@@ -5,57 +5,14 @@
#ifndef CHROME_BROWSER_CHROMEOS_CERT_PROVISIONING_CERT_PROVISIONING_COMMON_H_
#define CHROME_BROWSER_CHROMEOS_CERT_PROVISIONING_CERT_PROVISIONING_COMMON_H_
#include <string>
#include "chromeos/dbus/constants/attestation_constants.h"
#include "components/policy/proto/device_management_backend.pb.h"
class PrefRegistrySimple;
namespace chromeos {
namespace cert_provisioning {
const char kKeyNamePrefix[] = "cert-provis-";
// The type for variables containing an error from DM Server response.
using CertProvisioningResponseErrorType =
enterprise_management::ClientCertificateProvisioningResponse::Error;
// The namespace that contains convenient aliases for error values, e.g.
// UNDEFINED, TIMED_OUT, IDENTITY_VERIFICATION_ERROR, CA_ERROR.
using CertProvisioningResponseError =
enterprise_management::ClientCertificateProvisioningResponse;
// Numeric values are used in serialization and should not be remapped.
enum class CertScope { kUser = 0, kDevice = 1, kMaxValue = kDevice };
using CertProfileId = std::string;
struct CertProfile {
CertProfileId profile_id;
};
void RegisterProfilePrefs(PrefRegistrySimple* registry);
void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
// Returns the nickname (CKA_LABEL) for keys created for the |profile_id|.
std::string GetKeyName(CertProfileId profile_id);
// Returns the key type for VA API calls for |scope|.
attestation::AttestationKeyType GetVaKeyType(CertScope scope);
const char* GetPlatformKeysTokenId(CertScope scope);
// The Verified Access APIs are used to generate keypairs. For user-specific
// keypairs, it is possible to reuse the keypair that is used for Verified
// Access challenge response generation and name it with a custom name. For
// device-wide keypairs, the keypair used for Verified Access challenge response
// generation must be stable, but an additional keypair can be embedded
// (key_name_for_spkac). See
// http://go/chromeos-va-registering-device-wide-keys-support for details. For
// these reasons, the name of key that should be registered and will be used for
// certificate provisioning is passed as key_name for user-specific keys and
// key_name_for_spkac for device-wide keys.
std::string GetVaKeyName(CertScope scope, CertProfileId profile_id);
std::string GetVaKeyNameForSpkac(CertScope scope, CertProfileId profile_id);
} // namespace cert_provisioning
} // namespace chromeos
......
// 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/chromeos/cert_provisioning/cert_provisioning_worker.h"
#include "base/no_destructor.h"
#include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
#include "chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "components/policy/core/common/cloud/cloud_policy_client.h"
#include "components/policy/core/common/cloud/device_management_service.h"
#include "content/public/browser/browser_context.h"
namespace em = enterprise_management;
namespace chromeos {
namespace cert_provisioning {
namespace {
const base::TimeDelta kMinumumTryAgainLaterDelay =
base::TimeDelta::FromSeconds(10);
const net::BackoffEntry::Policy kBackoffPolicy{
/*num_errors_to_ignore=*/0,
/*initial_delay_ms=*/30 * 1000 /* (30 seconds) */,
/*multiply_factor=*/2.0,
/*jitter_factor=*/0.15,
/*maximum_backoff_ms=*/12 * 60 * 60 * 1000 /* (12 hours) */,
/*entry_lifetime_ms=*/-1,
/*always_use_initial_delay=*/false};
std::string CertScopeToString(CertScope cert_scope) {
switch (cert_scope) {
case CertScope::kUser:
return "google/chromeos/user";
case CertScope::kDevice:
return "google/chromeos/device";
}
NOTREACHED();
}
bool ConvertHashingAlgorithm(
em::HashingAlgorithm input_algo,
base::Optional<chromeos::platform_keys::HashAlgorithm>* output_algo) {
switch (input_algo) {
case em::HashingAlgorithm::SHA1:
*output_algo =
chromeos::platform_keys::HashAlgorithm::HASH_ALGORITHM_SHA1;
return true;
case em::HashingAlgorithm::SHA256:
*output_algo =
chromeos::platform_keys::HashAlgorithm::HASH_ALGORITHM_SHA256;
return true;
case em::HashingAlgorithm::HASHING_ALGORITHM_UNSPECIFIED:
return false;
}
}
// States are used in serialization and cannot be reordered. Therefore, their
// order should not be defined by their underlying values.
int GetStateOrderedIndex(CertProvisioningWorkerState state) {
int res = 0;
switch (state) {
case CertProvisioningWorkerState::kInitState:
res -= 1;
FALLTHROUGH;
case CertProvisioningWorkerState::kKeypairGenerated:
res -= 1;
FALLTHROUGH;
case CertProvisioningWorkerState::kStartCsrResponseReceived:
res -= 1;
FALLTHROUGH;
case CertProvisioningWorkerState::kVaChallengeFinished:
res -= 1;
FALLTHROUGH;
case CertProvisioningWorkerState::kKeyRegistered:
res -= 1;
FALLTHROUGH;
case CertProvisioningWorkerState::kSignCsrFinished:
res -= 1;
FALLTHROUGH;
case CertProvisioningWorkerState::kFinishCsrResponseReceived:
res -= 1;
FALLTHROUGH;
case CertProvisioningWorkerState::kDownloadCertResponseReceived:
res -= 1;
FALLTHROUGH;
case CertProvisioningWorkerState::kSucceed:
case CertProvisioningWorkerState::kFailed:
res -= 1;
}
return res;
}
} // namespace
// ============= CertProvisioningWorkerFactory =================================
CertProvisioningWorkerFactory* CertProvisioningWorkerFactory::test_factory_ =
nullptr;
// static
CertProvisioningWorkerFactory* CertProvisioningWorkerFactory::Get() {
if (UNLIKELY(test_factory_)) {
return test_factory_;
}
static base::NoDestructor<CertProvisioningWorkerFactory> factory;
return factory.get();
}
std::unique_ptr<CertProvisioningWorker> CertProvisioningWorkerFactory::Create(
CertScope cert_scope,
Profile* profile,
PrefService* pref_service,
const CertProfile& cert_profile,
policy::CloudPolicyClient* cloud_policy_client,
CertProvisioningWorkerCallback callback) {
return std::make_unique<CertProvisioningWorkerImpl>(
cert_scope, profile, pref_service, cert_profile, cloud_policy_client,
std::move(callback));
}
// static
void CertProvisioningWorkerFactory::SetFactoryForTesting(
CertProvisioningWorkerFactory* test_factory) {
test_factory_ = test_factory;
}
// ============= CertProvisioningWorkerImpl ====================================
CertProvisioningWorkerImpl::CertProvisioningWorkerImpl(
CertScope cert_scope,
Profile* profile,
PrefService* pref_service,
const CertProfile& cert_profile,
policy::CloudPolicyClient* cloud_policy_client,
CertProvisioningWorkerCallback callback)
: cert_scope_(cert_scope),
profile_(profile),
pref_service_(pref_service),
cert_profile_(cert_profile),
callback_(std::move(callback)),
request_backoff_(&kBackoffPolicy),
cloud_policy_client_(cloud_policy_client) {
CHECK(profile);
platform_keys_service_ =
platform_keys::PlatformKeysServiceFactory::GetForBrowserContext(profile);
CHECK(platform_keys_service_);
CHECK(pref_service);
CHECK(cloud_policy_client_);
}
CertProvisioningWorkerImpl::~CertProvisioningWorkerImpl() = default;
void CertProvisioningWorkerImpl::DoStep() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CancelScheduledTasks();
is_waiting_ = false;
switch (state_) {
case CertProvisioningWorkerState::kInitState:
GenerateKey();
return;
case CertProvisioningWorkerState::kKeypairGenerated:
StartCsr();
return;
case CertProvisioningWorkerState::kStartCsrResponseReceived:
BuildVaChallengeResponse();
return;
case CertProvisioningWorkerState::kVaChallengeFinished:
RegisterKey();
return;
case CertProvisioningWorkerState::kKeyRegistered:
SignCsr();
return;
case CertProvisioningWorkerState::kSignCsrFinished:
FinishCsr();
return;
case CertProvisioningWorkerState::kFinishCsrResponseReceived:
DownloadCert();
return;
case CertProvisioningWorkerState::kDownloadCertResponseReceived:
ImportCert();
return;
case CertProvisioningWorkerState::kSucceed:
case CertProvisioningWorkerState::kFailed:
DCHECK(false);
return;
}
NOTREACHED() << " " << static_cast<uint>(state_);
}
void CertProvisioningWorkerImpl::UpdateState(
CertProvisioningWorkerState new_state) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(GetStateOrderedIndex(state_) < GetStateOrderedIndex(new_state));
state_ = new_state;
if (IsFinished()) {
std::move(callback_).Run(state_ == CertProvisioningWorkerState::kSucceed);
}
}
void CertProvisioningWorkerImpl::GenerateKey() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
tpm_challenge_key_subtle_impl_ =
attestation::TpmChallengeKeySubtleFactory::Create();
tpm_challenge_key_subtle_impl_->StartPrepareKeyStep(
GetVaKeyType(cert_scope_),
GetVaKeyName(cert_scope_, cert_profile_.profile_id), profile_,
GetVaKeyNameForSpkac(cert_scope_, cert_profile_.profile_id),
base::BindOnce(&CertProvisioningWorkerImpl::OnGenerateKeyDone,
weak_factory_.GetWeakPtr()));
}
void CertProvisioningWorkerImpl::OnGenerateKeyDone(
const attestation::TpmChallengeKeyResult& result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!result.IsSuccess() || result.public_key.empty()) {
LOG(ERROR) << "Failed to prepare key: " << result.GetErrorMessage();
UpdateState(CertProvisioningWorkerState::kFailed);
return;
}
public_key_ = result.public_key;
UpdateState(CertProvisioningWorkerState::kKeypairGenerated);
DoStep();
}
void CertProvisioningWorkerImpl::StartCsr() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
cloud_policy_client_->ClientCertProvisioningStartCsr(
CertScopeToString(cert_scope_), cert_profile_.profile_id, public_key_,
base::BindOnce(&CertProvisioningWorkerImpl::OnStartCsrDone,
weak_factory_.GetWeakPtr()));
}
void CertProvisioningWorkerImpl::OnStartCsrDone(
policy::DeviceManagementStatus status,
base::Optional<CertProvisioningResponseErrorType> error,
base::Optional<int64_t> try_later,
const std::string& invalidation_topic,
const std::string& va_challenge,
enterprise_management::HashingAlgorithm hashing_algorithm,
const std::string& data_to_sign) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!ProcessResponseErrors(status, error, try_later)) {
return;
}
if (!ConvertHashingAlgorithm(hashing_algorithm, &hashing_algorithm_)) {
LOG(ERROR) << "Failed to parse hashing algorithm";
UpdateState(CertProvisioningWorkerState::kFailed);
return;
}
csr_ = data_to_sign;
invalidation_topic_ = invalidation_topic;
va_challenge_ = va_challenge;
UpdateState(CertProvisioningWorkerState::kStartCsrResponseReceived);
RegisterForInvalidationTopic(invalidation_topic);
DoStep();
}
void CertProvisioningWorkerImpl::BuildVaChallengeResponse() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (va_challenge_.empty()) {
UpdateState(CertProvisioningWorkerState::kVaChallengeFinished);
DoStep();
return;
}
tpm_challenge_key_subtle_impl_->StartSignChallengeStep(
va_challenge_, /*include_signed_public_key=*/true,
base::BindOnce(
&CertProvisioningWorkerImpl::OnBuildVaChallengeResponseDone,
weak_factory_.GetWeakPtr()));
}
void CertProvisioningWorkerImpl::OnBuildVaChallengeResponseDone(
const attestation::TpmChallengeKeyResult& result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!result.IsSuccess()) {
LOG(ERROR) << "Failed to build challenge response: "
<< result.GetErrorMessage();
UpdateState(CertProvisioningWorkerState::kFailed);
return;
}
if (result.challenge_response.empty()) {
LOG(ERROR) << "Challenge response is empty";
UpdateState(CertProvisioningWorkerState::kFailed);
return;
}
va_challenge_response_ = result.challenge_response;
UpdateState(CertProvisioningWorkerState::kVaChallengeFinished);
DoStep();
}
void CertProvisioningWorkerImpl::RegisterKey() {
tpm_challenge_key_subtle_impl_->StartRegisterKeyStep(
base::BindOnce(&CertProvisioningWorkerImpl::OnRegisterKeyDone,
weak_factory_.GetWeakPtr()));
}
void CertProvisioningWorkerImpl::OnRegisterKeyDone(
const attestation::TpmChallengeKeyResult& result) {
if (!result.IsSuccess()) {
LOG(ERROR) << "Failed to register key: " << result.GetErrorMessage();
UpdateState(CertProvisioningWorkerState::kFailed);
return;
}
UpdateState(CertProvisioningWorkerState::kKeyRegistered);
DoStep();
}
void CertProvisioningWorkerImpl::SignCsr() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!hashing_algorithm_.has_value()) {
LOG(ERROR) << "Hashing algorithm is empty";
UpdateState(CertProvisioningWorkerState::kFailed);
return;
}
platform_keys_service_->SignRSAPKCS1Digest(
GetPlatformKeysTokenId(cert_scope_), csr_, public_key_,
hashing_algorithm_.value(),
base::BindRepeating(&CertProvisioningWorkerImpl::OnSignCsrDone,
weak_factory_.GetWeakPtr()));
}
void CertProvisioningWorkerImpl::OnSignCsrDone(
const std::string& signature,
const std::string& error_message) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!error_message.empty()) {
LOG(ERROR) << "Failed to sign CSR: " << error_message;
UpdateState(CertProvisioningWorkerState::kFailed);
return;
}
signature_ = signature;
UpdateState(CertProvisioningWorkerState::kSignCsrFinished);
DoStep();
}
void CertProvisioningWorkerImpl::FinishCsr() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
cloud_policy_client_->ClientCertProvisioningFinishCsr(
CertScopeToString(cert_scope_), cert_profile_.profile_id, public_key_,
va_challenge_response_, signature_,
base::BindOnce(&CertProvisioningWorkerImpl::OnFinishCsrDone,
weak_factory_.GetWeakPtr()));
}
void CertProvisioningWorkerImpl::OnFinishCsrDone(
policy::DeviceManagementStatus status,
base::Optional<CertProvisioningResponseErrorType> error,
base::Optional<int64_t> try_later) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!ProcessResponseErrors(status, error, try_later)) {
return;
}
UpdateState(CertProvisioningWorkerState::kFinishCsrResponseReceived);
DoStep();
}
void CertProvisioningWorkerImpl::DownloadCert() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
cloud_policy_client_->ClientCertProvisioningDownloadCert(
CertScopeToString(cert_scope_), cert_profile_.profile_id, public_key_,
base::BindOnce(&CertProvisioningWorkerImpl::OnDownloadCertDone,
weak_factory_.GetWeakPtr()));
}
void CertProvisioningWorkerImpl::OnDownloadCertDone(
policy::DeviceManagementStatus status,
base::Optional<CertProvisioningResponseErrorType> error,
base::Optional<int64_t> try_later,
const std::string& pem_encoded_certificate) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!ProcessResponseErrors(status, error, try_later)) {
return;
}
pem_encoded_certificate_ = pem_encoded_certificate;
UpdateState(CertProvisioningWorkerState::kDownloadCertResponseReceived);
DoStep();
}
void CertProvisioningWorkerImpl::ImportCert() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
net::CertificateList cert_list =
net::X509Certificate::CreateCertificateListFromBytes(
pem_encoded_certificate_.data(), pem_encoded_certificate_.size(),
net::X509Certificate::FORMAT_AUTO);
if (cert_list.size() != 1) {
LOG(ERROR) << "Unexpected certificate content: size " << cert_list.size();
UpdateState(CertProvisioningWorkerState::kFailed);
return;
}
platform_keys_service_->ImportCertificate(
GetPlatformKeysTokenId(cert_scope_), cert_list.front(),
base::BindRepeating(&CertProvisioningWorkerImpl::OnImportCertDone,
weak_factory_.GetWeakPtr()));
}
void CertProvisioningWorkerImpl::OnImportCertDone(
const std::string& error_message) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!error_message.empty()) {
LOG(ERROR) << "Failed to import certificate: " << error_message;
UpdateState(CertProvisioningWorkerState::kFailed);
return;
}
UpdateState(CertProvisioningWorkerState::kSucceed);
}
bool CertProvisioningWorkerImpl::IsFinished() const {
switch (state_) {
case CertProvisioningWorkerState::kSucceed:
case CertProvisioningWorkerState::kFailed:
return true;
default:
return false;
}
}
bool CertProvisioningWorkerImpl::IsWaiting() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return is_waiting_;
}
CertProvisioningWorkerState CertProvisioningWorkerImpl::GetState() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return state_;
}
bool CertProvisioningWorkerImpl::ProcessResponseErrors(
policy::DeviceManagementStatus status,
base::Optional<CertProvisioningResponseErrorType> error,
base::Optional<int64_t> try_later) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (status ==
policy::DeviceManagementStatus::DM_STATUS_TEMPORARY_UNAVAILABLE) {
LOG(WARNING) << "DM Server is temporary unavailable";
request_backoff_.InformOfRequest(false);
ScheduleNextStep(request_backoff_.GetTimeUntilRelease());
return false;
}
if (status != policy::DeviceManagementStatus::DM_STATUS_SUCCESS) {
LOG(ERROR) << "DM Server returned error: " << status;
UpdateState(CertProvisioningWorkerState::kFailed);
return false;
}
request_backoff_.InformOfRequest(true);
if (error.has_value()) {
LOG(ERROR) << "Server response contains error: " << error.value();
UpdateState(CertProvisioningWorkerState::kFailed);
return false;
}
if (try_later.has_value()) {
ScheduleNextStep(base::TimeDelta::FromMilliseconds(try_later.value()));
return false;
}
return true;
}
void CertProvisioningWorkerImpl::ScheduleNextStep(base::TimeDelta delay) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
delay = std::max(delay, kMinumumTryAgainLaterDelay);
base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::Bind(&CertProvisioningWorkerImpl::DoStep,
weak_factory_.GetWeakPtr()),
delay);
is_waiting_ = true;
VLOG(0) << "Next step scheduled in " << delay;
}
void CertProvisioningWorkerImpl::CancelScheduledTasks() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
weak_factory_.InvalidateWeakPtrs();
}
} // namespace cert_provisioning
} // namespace chromeos
// 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.
#ifndef CHROME_BROWSER_CHROMEOS_CERT_PROVISIONING_CERT_PROVISIONING_WORKER_H_
#define CHROME_BROWSER_CHROMEOS_CERT_PROVISIONING_CERT_PROVISIONING_WORKER_H_
#include <memory>
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "chrome/browser/chromeos/attestation/tpm_challenge_key_subtle.h"
#include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h"
#include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "net/base/backoff_entry.h"
namespace policy {
class CloudPolicyClient;
}
class Profile;
class PrefService;
namespace chromeos {
namespace cert_provisioning {
using CertProvisioningWorkerCallback =
base::OnceCallback<void(bool is_success)>;
class CertProvisioningWorker;
class CertProvisioningWorkerFactory {
public:
virtual ~CertProvisioningWorkerFactory() = default;
static CertProvisioningWorkerFactory* Get();
virtual std::unique_ptr<CertProvisioningWorker> Create(
CertScope cert_scope,
Profile* profile,
PrefService* pref_service,
const CertProfile& cert_profile,
policy::CloudPolicyClient* cloud_policy_client,
CertProvisioningWorkerCallback callback);
// Doesn't take ownership.
static void SetFactoryForTesting(CertProvisioningWorkerFactory* test_factory);
private:
static CertProvisioningWorkerFactory* test_factory_;
};
// These values are used in serialization and should be changed carefully.
enum class CertProvisioningWorkerState {
kInitState = 0,
kKeypairGenerated = 1,
kStartCsrResponseReceived = 2,
kVaChallengeFinished = 3,
kKeyRegistered = 4,
kSignCsrFinished = 5,
kFinishCsrResponseReceived = 6,
kDownloadCertResponseReceived = 7,
kSucceed = 8,
kFailed = 9,
kMaxValue = kFailed,
};
class CertProvisioningWorker {
public:
CertProvisioningWorker() = default;
CertProvisioningWorker(const CertProvisioningWorker&) = delete;
CertProvisioningWorker& operator=(const CertProvisioningWorker&) = delete;
virtual ~CertProvisioningWorker() = default;
// Continue provisioning a certificate.
virtual void DoStep() = 0;
// Returns true, if the worker is waiting for some future event. |DoStep| can
// be called to try continue right now.
virtual bool IsWaiting() const = 0;
};
class CertProvisioningWorkerImpl : public CertProvisioningWorker {
public:
CertProvisioningWorkerImpl(CertScope cert_scope,
Profile* profile,
PrefService* pref_service,
const CertProfile& cert_profile,
policy::CloudPolicyClient* cloud_policy_client,
CertProvisioningWorkerCallback callback);
~CertProvisioningWorkerImpl() override;
// CertProvisioningWorker
void DoStep() override;
bool IsWaiting() const override;
// For testing.
CertProvisioningWorkerState GetState() const;
private:
friend class CertProvisioningSerializer;
void GenerateKey();
void OnGenerateKeyDone(const attestation::TpmChallengeKeyResult& result);
void StartCsr();
void OnStartCsrDone(policy::DeviceManagementStatus status,
base::Optional<CertProvisioningResponseErrorType> error,
base::Optional<int64_t> try_later,
const std::string& invalidation_topic,
const std::string& va_challenge,
enterprise_management::HashingAlgorithm hashing_algorithm,
const std::string& data_to_sign);
void BuildVaChallengeResponse();
void OnBuildVaChallengeResponseDone(
const attestation::TpmChallengeKeyResult& result);
void RegisterKey();
void OnRegisterKeyDone(const attestation::TpmChallengeKeyResult& result);
void SignCsr();
void OnSignCsrDone(const std::string& signature,
const std::string& error_message);
void FinishCsr();
void OnFinishCsrDone(policy::DeviceManagementStatus status,
base::Optional<CertProvisioningResponseErrorType> error,
base::Optional<int64_t> try_later);
void DownloadCert();
void OnDownloadCertDone(
policy::DeviceManagementStatus status,
base::Optional<CertProvisioningResponseErrorType> error,
base::Optional<int64_t> try_later,
const std::string& pem_encoded_certificate);
void ImportCert();
void OnImportCertDone(const std::string& error_message);
void ScheduleNextStep(base::TimeDelta delay);
void CancelScheduledTasks();
// TODO: implement when invalidations are supported for cert provisioning.
void RegisterForInvalidationTopic(const std::string& invalidation_topic) {}
// If it is called with kSucceed or kFailed, it will call the |callback_|. The
// worker can be destroyed in callback and should not use any member fields
// after that.
void UpdateState(CertProvisioningWorkerState state);
bool IsFinished() const;
// Returns true if there are no errors and the flow can be continued.
bool ProcessResponseErrors(
policy::DeviceManagementStatus status,
base::Optional<CertProvisioningResponseErrorType> error,
base::Optional<int64_t> try_later);
CertScope cert_scope_ = CertScope::kUser;
Profile* profile_ = nullptr;
PrefService* pref_service_ = nullptr;
CertProfile cert_profile_;
CertProvisioningWorkerCallback callback_;
// This field should be updated only via |UpdateState| function. It will
// trigger update of the serialized data.
CertProvisioningWorkerState state_ = CertProvisioningWorkerState::kInitState;
bool is_waiting_ = false;
// Currently it is used only for DM Server DM_STATUS_TEMPORARY_UNAVAILABLE
// error. For all other errors the worker just gives up.
net::BackoffEntry request_backoff_;
std::string public_key_;
std::string invalidation_topic_;
std::string csr_;
std::string va_challenge_;
std::string va_challenge_response_;
base::Optional<platform_keys::HashAlgorithm> hashing_algorithm_;
std::string signature_;
std::string pem_encoded_certificate_;
platform_keys::PlatformKeysService* platform_keys_service_ = nullptr;
std::unique_ptr<attestation::TpmChallengeKeySubtle>
tpm_challenge_key_subtle_impl_;
policy::CloudPolicyClient* cloud_policy_client_ = nullptr;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<CertProvisioningWorkerImpl> weak_factory_{this};
};
} // namespace cert_provisioning
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_CERT_PROVISIONING_CERT_PROVISIONING_WORKER_H_
// 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/chromeos/cert_provisioning/cert_provisioning_worker.h"
#include "base/callback.h"
#include "base/test/gmock_callback_support.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/attestation/mock_tpm_challenge_key_subtle.h"
#include "chrome/browser/chromeos/attestation/tpm_challenge_key_subtle.h"
#include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h"
#include "chrome/browser/chromeos/platform_keys/mock_platform_keys_service.h"
#include "chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
#include "components/prefs/testing_pref_service.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace em = enterprise_management;
using base::TimeDelta;
using base::test::RunOnceCallback;
using testing::_;
using testing::Mock;
namespace chromeos {
namespace cert_provisioning {
namespace {
// Generated by chrome/test/data/policy/test_certs/create_test_certs.sh
const char kFakeCertificate[] = R"(-----BEGIN CERTIFICATE-----
MIIDJzCCAg+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxyb290
X2NhX2NlcnQwHhcNMjAwMjI1MTUyNTU2WhcNMzAwMjIyMTUyNTU2WjAUMRIwEAYD
VQQDDAkxMjcuMC4wLjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDW
druvpaJovmyWzIcjtsSk/lp319+zNPSYGLzJzTeEmnFoDf/b89ft6xR1NIahmvVd
UHGOMlzgDKnNkqWw+pgpn6U8dk+leWnwlUefzDz7OY8qXfX29Vh0m/kATQc64lnp
rX19fEi2DOgH6heCQDSaHI/KAnAXccwl8kdGuTEnvdzbdHqQq8pPGpEqzC/NOjk7
kDNkUt0J74ZVMm4+jhVOgZ35mFLtC+xjfycBgbnt8yfPOzmOMwXTjYDPNaIy32AZ
t66oIToteoW5Ilg+j5Mto3unBDHrw8rml3+W/nwHuOPEIgBqLQFfWtXpuX8CbcS6
SFNK4hxCJOvlzUbgTpsrAgMBAAGjgYAwfjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQW
BBRDEl1/2pL5LtKnpIly+XCj3N6MwDAfBgNVHSMEGDAWgBQrwVEnUQZlX850A2N+
URfS8BxoyzAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0RBAgw
BocEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEAXZd+Ul7GUFZPLSiTZ618hUI2UdO0
7rtPwBw3TephWuyEeHht+WhzA3sRL3nprEiJqIg5w/Tlfz4dsObpSU3vKmDhLzAx
HJrN5vKdbEj9wyuhYSRJwvJka1ZOgPzhQcDQOp1SqonNxLx/sSMDR2UIDMBGzrkQ
sDkn58N5eWm+hZADOAKROHR47j85VcsmYGK7z2x479YzsyWyOm0dbACXv7/HvFkz
56KvgxRaPZQzQUg5yuXa21IjQz07wyWSYnHpm2duAbYFl6CTR9Rlj5vpRkKsQP1W
mMhGDBfgEskdbM+0agsZrJupoQMBUbD5gflcJlW3kwlboi3dTtiGixfYWw==
-----END CERTIFICATE-----)";
const char kCertProfileId[] = "cert_profile_1";
// Prefix + certificate profile name.
const char kKeyName[] = "cert-provis-cert_profile_1";
const char kCertScopeStrUser[] = "google/chromeos/user";
const char kCertScopeStrDevice[] = "google/chromeos/device";
const char kPublicKey[] = "fake_public_key_1";
const char kInvalidationTopic[] = "fake_invalidation_topic_1";
const char kDataToSign[] = "fake_data_to_sign_1";
const em::HashingAlgorithm kProtoHashAlgo = em::HashingAlgorithm::SHA256;
const platform_keys::HashAlgorithm kPkHashAlgo =
platform_keys::HashAlgorithm::HASH_ALGORITHM_SHA256;
const char kChallenge[] = "fake_va_challenge_1";
const char kChallengeResponse[] = "fake_va_challenge_response_1";
const char kSignature[] = "fake_signature_1";
// Using macros to reduce boilerplate code, but keep real line numbers in
// error messages in case of expectation failure. They use some of protected
// fields of CertProvisioningWorkerTest class and may be considered as extra
// methods of it.
#define EXPECT_PREPARE_KEY_OK(PREPARE_KEY_FUNC) \
{ \
auto public_key_result = \
attestation::TpmChallengeKeyResult::MakePublicKey(kPublicKey); \
EXPECT_CALL(*tpm_challenge_key_impl_, PREPARE_KEY_FUNC) \
.Times(1) \
.WillOnce(RunOnceCallback<4>(public_key_result)); \
}
#define EXPECT_SIGN_CHALLENGE_OK(SIGN_CHALLENGE_FUNC) \
{ \
auto sign_challenge_result = \
attestation::TpmChallengeKeyResult::MakeChallengeResponse( \
kChallengeResponse); \
EXPECT_CALL(*tpm_challenge_key_impl_, SIGN_CHALLENGE_FUNC) \
.Times(1) \
.WillOnce(RunOnceCallback<2>(sign_challenge_result)); \
}
#define EXPECT_REGISTER_KEY_OK(REGISTER_KEY_FUNC) \
{ \
auto register_key_result = \
attestation::TpmChallengeKeyResult::MakeSuccess(); \
EXPECT_CALL(*tpm_challenge_key_impl_, REGISTER_KEY_FUNC) \
.Times(1) \
.WillOnce(RunOnceCallback<0>(register_key_result)); \
}
#define EXPECT_START_CSR_OK(START_CSR_FUNC) \
{ \
EXPECT_CALL(cloud_policy_client_, START_CSR_FUNC) \
.Times(1) \
.WillOnce(RunOnceCallback<3>( \
policy::DeviceManagementStatus::DM_STATUS_SUCCESS, \
/*response_error=*/base::nullopt, \
/*try_again_later_ms=*/base::nullopt, kInvalidationTopic, \
kChallenge, kProtoHashAlgo, kDataToSign)); \
}
#define EXPECT_START_CSR_OK_WITHOUT_VA(START_CSR_FUNC) \
{ \
EXPECT_CALL(cloud_policy_client_, START_CSR_FUNC) \
.Times(1) \
.WillOnce(RunOnceCallback<3>( \
policy::DeviceManagementStatus::DM_STATUS_SUCCESS, \
/*response_error=*/base::nullopt, \
/*try_again_later_ms=*/base::nullopt, kInvalidationTopic, \
/*va_challenge=*/"", kProtoHashAlgo, kDataToSign)); \
}
#define EXPECT_START_CSR_TRY_LATER(START_CSR_FUNC, DELAY_MS) \
{ \
EXPECT_CALL(cloud_policy_client_, START_CSR_FUNC) \
.Times(1) \
.WillOnce(RunOnceCallback<3>( \
policy::DeviceManagementStatus::DM_STATUS_SUCCESS, \
/*response_error=*/base::nullopt, \
/*try_again_later_ms=*/(DELAY_MS), /*invalidation_topic=*/"", \
/*va_challenge=*/"", \
enterprise_management::HashingAlgorithm:: \
HASHING_ALGORITHM_UNSPECIFIED, \
/*data_to_sign=*/"")); \
}
#define EXPECT_START_CSR_INVALID_REQUEST(START_CSR_FUNC) \
{ \
EXPECT_CALL(cloud_policy_client_, START_CSR_FUNC) \
.Times(1) \
.WillOnce(RunOnceCallback<3>( \
policy::DeviceManagementStatus::DM_STATUS_REQUEST_INVALID, \
/*response_error=*/base::nullopt, \
/*try_again_later_ms=*/base::nullopt, /*invalidation_topic=*/"", \
/*va_challenge=*/"", \
enterprise_management::HashingAlgorithm:: \
HASHING_ALGORITHM_UNSPECIFIED, \
/*data_to_sign=*/"")); \
}
#define EXPECT_START_CSR_CA_ERROR(START_CSR_FUNC) \
{ \
EXPECT_CALL(cloud_policy_client_, START_CSR_FUNC) \
.Times(1) \
.WillOnce(RunOnceCallback<3>( \
policy::DeviceManagementStatus::DM_STATUS_SUCCESS, \
/*response_error=*/CertProvisioningResponseError::CA_ERROR, \
/*try_again_later_ms=*/base::nullopt, /*invalidation_topic=*/"", \
/*va_challenge=*/"", \
enterprise_management::HashingAlgorithm:: \
HASHING_ALGORITHM_UNSPECIFIED, \
/*data_to_sign=*/"")); \
}
#define EXPECT_START_CSR_TEMPORARY_UNAVAILABLE(START_CSR_FUNC) \
{ \
EXPECT_CALL(cloud_policy_client_, START_CSR_FUNC) \
.Times(1) \
.WillOnce(RunOnceCallback<3>( \
policy::DeviceManagementStatus::DM_STATUS_TEMPORARY_UNAVAILABLE, \
/*response_error=*/base::nullopt, \
/*try_again_later_ms=*/base::nullopt, /*invalidation_topic=*/"", \
/*va_challenge=*/"", \
enterprise_management::HashingAlgorithm:: \
HASHING_ALGORITHM_UNSPECIFIED, \
/*data_to_sign=*/"")); \
}
#define EXPECT_FINISH_CSR_OK(FINISH_CSR_FUNC) \
{ \
EXPECT_CALL(cloud_policy_client_, FINISH_CSR_FUNC) \
.Times(1) \
.WillOnce(RunOnceCallback<5>( \
policy::DeviceManagementStatus::DM_STATUS_SUCCESS, base::nullopt, \
base::nullopt)); \
}
#define EXPECT_FINISH_CSR_TRY_LATER(FINISH_CSR_FUNC, DELAY_MS) \
{ \
EXPECT_CALL(cloud_policy_client_, FINISH_CSR_FUNC) \
.Times(1) \
.WillOnce(RunOnceCallback<5>( \
policy::DeviceManagementStatus::DM_STATUS_SUCCESS, base::nullopt, \
/*try_again_later_ms=*/(DELAY_MS))); \
}
#define EXPECT_DOWNLOAD_CERT_OK(DOWNLOAD_CERT_FUNC) \
{ \
EXPECT_CALL(cloud_policy_client_, DOWNLOAD_CERT_FUNC) \
.Times(1) \
.WillOnce(RunOnceCallback<3>( \
policy::DeviceManagementStatus::DM_STATUS_SUCCESS, base::nullopt, \
base::nullopt, kFakeCertificate)); \
}
#define EXPECT_DOWNLOAD_CERT_TRY_LATER(DOWNLOAD_CERT_FUNC, DELAY_MS) \
{ \
EXPECT_CALL(cloud_policy_client_, DOWNLOAD_CERT_FUNC) \
.Times(1) \
.WillOnce(RunOnceCallback<3>( \
policy::DeviceManagementStatus::DM_STATUS_SUCCESS, base::nullopt, \
/*try_again_later_ms=*/(DELAY_MS), /*certificate=*/"")); \
}
#define EXPECT_SIGN_RSAPKC1_DIGEST_OK(SIGN_FUNC) \
{ \
EXPECT_CALL(*platform_keys_service_, SIGN_FUNC) \
.Times(1) \
.WillOnce(RunOnceCallback<4>(kSignature, /*error_message=*/"")); \
}
#define EXPECT_IMPORT_CERTIFICATE_OK(IMPORT_FUNC) \
{ \
EXPECT_CALL(*platform_keys_service_, IMPORT_FUNC) \
.Times(1) \
.WillOnce(RunOnceCallback<2>(/*error_message=*/"")); \
}
class CallbackObserver {
public:
MOCK_METHOD(void, Callback, (bool is_success));
};
class CertProvisioningWorkerTest : public ::testing::Test {
public:
CertProvisioningWorkerTest() { Init(); }
CertProvisioningWorkerTest(const CertProvisioningWorkerTest&) = delete;
CertProvisioningWorkerTest& operator=(const CertProvisioningWorkerTest&) =
delete;
~CertProvisioningWorkerTest() override = default;
void SetUp() override {
// Replace TpmChallengeKey.
auto mock_tpm_challenge_key_subtle_impl =
std::make_unique<attestation::MockTpmChallengeKeySubtle>();
tpm_challenge_key_impl_ = mock_tpm_challenge_key_subtle_impl.get();
attestation::TpmChallengeKeySubtleFactory::SetForTesting(
std::move(mock_tpm_challenge_key_subtle_impl));
ASSERT_TRUE(tpm_challenge_key_impl_);
// There should not be any calls to callback before this expect is
// overridden.
EXPECT_CALL(callback_observer_, Callback).Times(0);
}
void TearDown() override {
EXPECT_FALSE(
attestation::TpmChallengeKeySubtleFactory::WillReturnTestingInstance());
}
protected:
void Init() {
testing_profile_manager_ = std::make_unique<TestingProfileManager>(
TestingBrowserProcess::GetGlobal());
ASSERT_TRUE(testing_profile_manager_->SetUp());
testing_profile_ =
testing_profile_manager_->CreateTestingProfile("user@gmail.com");
ASSERT_TRUE(testing_profile_);
platform_keys_service_ =
static_cast<platform_keys::MockPlatformKeysService*>(
platform_keys::PlatformKeysServiceFactory::GetInstance()
->SetTestingFactoryAndUse(
testing_profile_,
base::BindRepeating(
&platform_keys::BuildMockPlatformKeysService)));
ASSERT_TRUE(platform_keys_service_);
}
void FastForwardBy(TimeDelta delta) {
task_environment_.FastForwardBy(delta);
}
CertProvisioningWorkerCallback GetCallback() {
return base::BindOnce(&CallbackObserver::Callback,
base::Unretained(&callback_observer_));
}
content::BrowserTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
CallbackObserver callback_observer_;
std::unique_ptr<TestingProfileManager> testing_profile_manager_;
TestingProfile* testing_profile_ = nullptr;
TestingPrefServiceSimple testing_pref_service_;
policy::MockCloudPolicyClient cloud_policy_client_;
attestation::MockTpmChallengeKeySubtle* tpm_challenge_key_impl_ = nullptr;
platform_keys::MockPlatformKeysService* platform_keys_service_ = nullptr;
};
// Checks that the worker makes all necessary requests to other modules during
// success scenario.
TEST_F(CertProvisioningWorkerTest, Success) {
CertProfile cert_profile{kCertProfileId};
CertProvisioningWorkerImpl worker(CertScope::kUser, testing_profile_,
&testing_pref_service_, cert_profile,
&cloud_policy_client_, GetCallback());
{
testing::InSequence seq;
EXPECT_PREPARE_KEY_OK(StartPrepareKeyStep(
attestation::AttestationKeyType::KEY_USER, kKeyName,
/*profile=*/_, /*key_name_for_spkac=*/"", /*callback=*/_));
EXPECT_START_CSR_OK(ClientCertProvisioningStartCsr(
kCertScopeStrUser, kCertProfileId, kPublicKey, /*callback=*/_));
EXPECT_SIGN_CHALLENGE_OK(StartSignChallengeStep(
kChallenge, /*include_signed_public_key=*/true, /*callback=*/_));
EXPECT_REGISTER_KEY_OK(StartRegisterKeyStep);
EXPECT_SIGN_RSAPKC1_DIGEST_OK(
SignRSAPKCS1Digest(platform_keys::kTokenIdUser, kDataToSign, kPublicKey,
kPkHashAlgo, /*callback=*/_));
EXPECT_FINISH_CSR_OK(ClientCertProvisioningFinishCsr(
kCertScopeStrUser, kCertProfileId, kPublicKey, kChallengeResponse,
kSignature, /*callback=*/_));
EXPECT_DOWNLOAD_CERT_OK(ClientCertProvisioningDownloadCert(
kCertScopeStrUser, kCertProfileId, kPublicKey, /*callback=*/_));
EXPECT_IMPORT_CERTIFICATE_OK(ImportCertificate(
platform_keys::kTokenIdUser, /*certificate=*/_, /*callback=*/_));
}
EXPECT_CALL(callback_observer_, Callback(true)).Times(1);
worker.DoStep();
}
// Checks that the worker makes all necessary requests to other modules during
// success scenario when VA challenge is not received.
TEST_F(CertProvisioningWorkerTest, NoVaSuccess) {
CertProfile cert_profile{kCertProfileId};
CertProvisioningWorkerImpl worker(CertScope::kUser, testing_profile_,
&testing_pref_service_, cert_profile,
&cloud_policy_client_, GetCallback());
EXPECT_CALL(*tpm_challenge_key_impl_, StartRegisterKeyStep).Times(0);
{
testing::InSequence seq;
EXPECT_PREPARE_KEY_OK(StartPrepareKeyStep(
attestation::AttestationKeyType::KEY_USER, kKeyName,
/*profile=*/_, /*key_name_for_spkac=*/"", /*callback=*/_));
EXPECT_START_CSR_OK_WITHOUT_VA(ClientCertProvisioningStartCsr(
kCertScopeStrUser, kCertProfileId, kPublicKey, /*callback=*/_));
EXPECT_REGISTER_KEY_OK(StartRegisterKeyStep);
EXPECT_SIGN_RSAPKC1_DIGEST_OK(
SignRSAPKCS1Digest(platform_keys::kTokenIdUser, kDataToSign, kPublicKey,
kPkHashAlgo, /*callback=*/_));
EXPECT_FINISH_CSR_OK(ClientCertProvisioningFinishCsr(
kCertScopeStrUser, kCertProfileId, kPublicKey,
/*va_challenge_response=*/"", kSignature, /*callback=*/_));
EXPECT_DOWNLOAD_CERT_OK(ClientCertProvisioningDownloadCert(
kCertScopeStrUser, kCertProfileId, kPublicKey, /*callback=*/_));
EXPECT_IMPORT_CERTIFICATE_OK(ImportCertificate(
platform_keys::kTokenIdUser, /*certificate=*/_, /*callback=*/_));
}
EXPECT_CALL(callback_observer_, Callback(true)).Times(1);
worker.DoStep();
}
// Checks that when the server returns try_again_later field, the worker will
// retry a request when it asked to continue the provisioning.
TEST_F(CertProvisioningWorkerTest, TryLaterManualRetry) {
CertProfile cert_profile{kCertProfileId};
CertProvisioningWorkerImpl worker(CertScope::kDevice, testing_profile_,
&testing_pref_service_, cert_profile,
&cloud_policy_client_, GetCallback());
const TimeDelta delay = TimeDelta::FromSeconds(30);
{
testing::InSequence seq;
EXPECT_PREPARE_KEY_OK(StartPrepareKeyStep(
attestation::AttestationKeyType::KEY_DEVICE, /*key_name=*/"",
/*profile=*/_, /*key_name_for_spkac=*/kKeyName, /*callback=*/_));
EXPECT_START_CSR_TRY_LATER(
ClientCertProvisioningStartCsr(kCertScopeStrDevice, kCertProfileId,
kPublicKey, /*callback=*/_),
delay.InMilliseconds());
worker.DoStep();
EXPECT_EQ(worker.GetState(),
CertProvisioningWorkerState::kKeypairGenerated);
}
{
testing::InSequence seq;
EXPECT_START_CSR_OK(ClientCertProvisioningStartCsr(
kCertScopeStrDevice, kCertProfileId, kPublicKey, /*callback=*/_));
EXPECT_SIGN_CHALLENGE_OK(StartSignChallengeStep(
kChallenge, /*include_signed_public_key=*/true, /*callback=*/_));
EXPECT_REGISTER_KEY_OK(StartRegisterKeyStep);
EXPECT_SIGN_RSAPKC1_DIGEST_OK(SignRSAPKCS1Digest);
EXPECT_FINISH_CSR_TRY_LATER(
ClientCertProvisioningFinishCsr(kCertScopeStrDevice, kCertProfileId,
kPublicKey, kChallengeResponse,
kSignature, /*callback=*/_),
delay.InMilliseconds());
worker.DoStep();
EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSignCsrFinished);
}
{
testing::InSequence seq;
EXPECT_FINISH_CSR_OK(ClientCertProvisioningFinishCsr(
kCertScopeStrDevice, kCertProfileId, kPublicKey, kChallengeResponse,
kSignature, /*callback=*/_));
EXPECT_DOWNLOAD_CERT_TRY_LATER(
ClientCertProvisioningDownloadCert(kCertScopeStrDevice, kCertProfileId,
kPublicKey, /*callback=*/_),
delay.InMilliseconds());
worker.DoStep();
EXPECT_EQ(worker.GetState(),
CertProvisioningWorkerState::kFinishCsrResponseReceived);
}
{
testing::InSequence seq;
EXPECT_DOWNLOAD_CERT_OK(ClientCertProvisioningDownloadCert(
kCertScopeStrDevice, kCertProfileId, kPublicKey, /*callback=*/_));
EXPECT_IMPORT_CERTIFICATE_OK(ImportCertificate(
platform_keys::kTokenIdSystem, /*certificate=*/_, /*callback=*/_));
EXPECT_CALL(callback_observer_, Callback(true)).Times(1);
worker.DoStep();
EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSucceed);
}
}
// Checks that when the server returns try_again_later field, the worker will
// automatically retry a request after some time.
TEST_F(CertProvisioningWorkerTest, TryLaterWait) {
CertProfile cert_profile{kCertProfileId};
CertProvisioningWorkerImpl worker(CertScope::kUser, testing_profile_,
&testing_pref_service_, cert_profile,
&cloud_policy_client_, GetCallback());
const TimeDelta start_csr_delay = TimeDelta::FromSeconds(30);
const TimeDelta finish_csr_delay = TimeDelta::FromSeconds(30);
const TimeDelta download_cert_server_delay = TimeDelta::FromMilliseconds(100);
const TimeDelta download_cert_real_delay = TimeDelta::FromSeconds(10);
const TimeDelta small_delay = TimeDelta::FromMilliseconds(500);
{
testing::InSequence seq;
EXPECT_PREPARE_KEY_OK(StartPrepareKeyStep(
attestation::AttestationKeyType::KEY_USER, kKeyName,
/*profile=*/_, /*key_name_for_spkac=*/"", /*callback=*/_));
EXPECT_START_CSR_TRY_LATER(
ClientCertProvisioningStartCsr(kCertScopeStrUser, kCertProfileId,
kPublicKey, /*callback=*/_),
start_csr_delay.InMilliseconds());
worker.DoStep();
EXPECT_EQ(worker.GetState(),
CertProvisioningWorkerState::kKeypairGenerated);
}
{
testing::InSequence seq;
EXPECT_START_CSR_OK(ClientCertProvisioningStartCsr(
kCertScopeStrUser, kCertProfileId, kPublicKey, /*callback=*/_));
EXPECT_SIGN_CHALLENGE_OK(StartSignChallengeStep(
kChallenge, /*include_signed_public_key=*/true, /*callback=*/_));
EXPECT_REGISTER_KEY_OK(StartRegisterKeyStep);
EXPECT_SIGN_RSAPKC1_DIGEST_OK(
SignRSAPKCS1Digest(platform_keys::kTokenIdUser, kDataToSign, kPublicKey,
kPkHashAlgo, /*callback=*/_));
EXPECT_FINISH_CSR_TRY_LATER(
ClientCertProvisioningFinishCsr(kCertScopeStrUser, kCertProfileId,
kPublicKey, kChallengeResponse,
kSignature, /*callback=*/_),
finish_csr_delay.InMilliseconds());
FastForwardBy(start_csr_delay + small_delay);
EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSignCsrFinished);
}
{
testing::InSequence seq;
EXPECT_FINISH_CSR_OK(ClientCertProvisioningFinishCsr(
kCertScopeStrUser, kCertProfileId, kPublicKey, kChallengeResponse,
kSignature, /*callback=*/_));
EXPECT_DOWNLOAD_CERT_TRY_LATER(
ClientCertProvisioningDownloadCert(kCertScopeStrUser, kCertProfileId,
kPublicKey, /*callback=*/_),
download_cert_server_delay.InMilliseconds());
FastForwardBy(finish_csr_delay + small_delay);
EXPECT_EQ(worker.GetState(),
CertProvisioningWorkerState::kFinishCsrResponseReceived);
}
{
testing::InSequence seq;
EXPECT_DOWNLOAD_CERT_OK(ClientCertProvisioningDownloadCert);
EXPECT_IMPORT_CERTIFICATE_OK(ImportCertificate(
platform_keys::kTokenIdUser, /*certificate=*/_, /*callback=*/_));
FastForwardBy(small_delay);
// Check that minimum wait time is not too small even if the server
// has responded with a small one.
EXPECT_EQ(worker.GetState(),
CertProvisioningWorkerState::kFinishCsrResponseReceived);
EXPECT_CALL(callback_observer_, Callback(true)).Times(1);
FastForwardBy(download_cert_real_delay + small_delay);
EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSucceed);
}
}
// Checks that when the server returns error status, the worker will enter an
// error state and stop the provisioning.
TEST_F(CertProvisioningWorkerTest, StatusErrorHandling) {
CertProfile cert_profile{kCertProfileId};
CertProvisioningWorkerImpl worker(CertScope::kUser, testing_profile_,
&testing_pref_service_, cert_profile,
&cloud_policy_client_, GetCallback());
{
testing::InSequence seq;
EXPECT_PREPARE_KEY_OK(StartPrepareKeyStep(
attestation::AttestationKeyType::KEY_USER, kKeyName,
/*profile=*/_, /*key_name_for_spkac=*/"", /*callback=*/_));
EXPECT_START_CSR_INVALID_REQUEST(ClientCertProvisioningStartCsr(
kCertScopeStrUser, kCertProfileId, kPublicKey, /*callback=*/_));
}
EXPECT_CALL(callback_observer_, Callback(false)).Times(1);
worker.DoStep();
}
// Checks that when the server returns response error, the worker will enter an
// error state and stop the provisioning. Also check factory.
TEST_F(CertProvisioningWorkerTest, ResponseErrorHandling) {
CertProfile cert_profile{kCertProfileId};
auto worker = CertProvisioningWorkerFactory::Get()->Create(
CertScope::kUser, testing_profile_, &testing_pref_service_, cert_profile,
&cloud_policy_client_, GetCallback());
{
testing::InSequence seq;
EXPECT_PREPARE_KEY_OK(StartPrepareKeyStep(
attestation::AttestationKeyType::KEY_USER, kKeyName,
/*profile=*/_, /*key_name_for_spkac=*/"", /*callback=*/_));
EXPECT_START_CSR_CA_ERROR(ClientCertProvisioningStartCsr);
}
EXPECT_CALL(callback_observer_, Callback(false)).Times(1);
worker->DoStep();
}
// Checks that when the server returns TEMPORARY_UNAVAILABLE status code, the
// worker will automatically retry a request using exponential backoff strategy.
TEST_F(CertProvisioningWorkerTest, BackoffStrategy) {
CertProfile cert_profile{kCertProfileId};
CertProvisioningWorkerImpl worker(CertScope::kUser, testing_profile_,
&testing_pref_service_, cert_profile,
&cloud_policy_client_, GetCallback());
TimeDelta next_delay = TimeDelta::FromSeconds(30);
const TimeDelta small_delay = TimeDelta::FromMilliseconds(500);
{
testing::InSequence seq;
EXPECT_PREPARE_KEY_OK(StartPrepareKeyStep(
attestation::AttestationKeyType::KEY_USER, kKeyName,
/*profile=*/_, /*key_name_for_spkac=*/"", /*callback=*/_));
EXPECT_START_CSR_TEMPORARY_UNAVAILABLE(ClientCertProvisioningStartCsr(
kCertScopeStrUser, kCertProfileId, kPublicKey, /*callback=*/_));
worker.DoStep();
}
Mock::VerifyAndClearExpectations(&cloud_policy_client_);
{
EXPECT_START_CSR_TEMPORARY_UNAVAILABLE(ClientCertProvisioningStartCsr(
kCertScopeStrUser, kCertProfileId, kPublicKey, /*callback=*/_));
FastForwardBy(next_delay + small_delay * 10);
next_delay *= 2;
}
Mock::VerifyAndClearExpectations(&cloud_policy_client_);
{
EXPECT_START_CSR_TEMPORARY_UNAVAILABLE(ClientCertProvisioningStartCsr(
kCertScopeStrUser, kCertProfileId, kPublicKey, /*callback=*/_));
FastForwardBy(next_delay + small_delay * 10);
next_delay *= 2;
}
Mock::VerifyAndClearExpectations(&cloud_policy_client_);
{
EXPECT_START_CSR_TEMPORARY_UNAVAILABLE(ClientCertProvisioningStartCsr(
kCertScopeStrUser, kCertProfileId, kPublicKey, /*callback=*/_));
FastForwardBy(next_delay + small_delay);
next_delay *= 2;
}
}
} // namespace
} // namespace cert_provisioning
} // namespace chromeos
......@@ -728,9 +728,7 @@ void CloudPolicyClient::ClientCertProvisioningFinishCsr(
em::FinishCsrRequest* finish_csr_request =
request->mutable_finish_csr_request();
if (!va_challenge_response.empty()) {
finish_csr_request->set_va_challenge_response(va_challenge_response);
}
finish_csr_request->set_signature(signature);
request_jobs_.push_back(service_->CreateJob(std::move(config)));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment