blob: 7031313b140627c3644a897445d4b535605d083e [file] [log] [blame]
// 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 "chromeos/attestation/attestation_flow_integrated.h"
#include <algorithm>
#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/optional.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/timer/timer.h"
#include "chromeos/attestation/attestation_flow_utils.h"
#include "chromeos/cryptohome/cryptohome_parameters.h"
#include "chromeos/dbus/attestation/attestation_client.h"
#include "chromeos/dbus/attestation/interface.pb.h"
#include "chromeos/dbus/constants/dbus_switches.h"
#include "components/account_id/account_id.h"
namespace chromeos {
namespace attestation {
namespace {
// A reasonable timeout that gives enough time for attestation to be ready,
// yet does not make the caller wait too long.
constexpr base::TimeDelta kReadyTimeout = base::TimeDelta::FromSeconds(60);
// Delay before checking again whether the TPM has been prepared for
// attestation.
constexpr base::TimeDelta kRetryDelay = base::TimeDelta::FromMilliseconds(300);
// Values for the attestation server switch.
constexpr char kAttestationServerDefault[] = "default";
constexpr char kAttestationServerTest[] = "test";
::attestation::ACAType GetConfiguredACAType() {
std::string value =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
chromeos::switches::kAttestationServer);
if (value.empty() || value == kAttestationServerDefault) {
return ::attestation::ACAType::DEFAULT_ACA;
}
if (value == kAttestationServerTest) {
return ::attestation::ACAType::TEST_ACA;
}
LOG(WARNING) << "Invalid attestation server value: " << value
<< "; using default.";
return ::attestation::ACAType::DEFAULT_ACA;
}
bool IsPreparedWith(const ::attestation::GetEnrollmentPreparationsReply& reply,
::attestation::ACAType aca_type) {
for (const auto& preparation : reply.enrollment_preparations()) {
if (preparation.first == aca_type) {
return preparation.second;
}
}
return false;
}
base::Optional<::attestation::CertificateProfile> ProfileToAttestationProtoEnum(
AttestationCertificateProfile p) {
switch (p) {
case PROFILE_ENTERPRISE_MACHINE_CERTIFICATE:
return ::attestation::CertificateProfile::ENTERPRISE_MACHINE_CERTIFICATE;
case PROFILE_ENTERPRISE_USER_CERTIFICATE:
return ::attestation::CertificateProfile::ENTERPRISE_USER_CERTIFICATE;
case PROFILE_CONTENT_PROTECTION_CERTIFICATE:
return ::attestation::CertificateProfile::CONTENT_PROTECTION_CERTIFICATE;
case PROFILE_ENTERPRISE_ENROLLMENT_CERTIFICATE:
return ::attestation::CertificateProfile::
ENTERPRISE_ENROLLMENT_CERTIFICATE;
default:
return {};
}
}
} // namespace
AttestationFlowIntegrated::AttestationFlowIntegrated()
: AttestationFlowIntegrated(GetConfiguredACAType()) {}
// This constructor passes |nullptr|s to the base class
// |AttestationFlow| because we don't use cryptohome client and server
// proxy in |AttestationFlowIntegrated|.
//
// TOOD(b/158955123): Remove this transitional state along with the removal of
// |AttestationFlow|.
AttestationFlowIntegrated::AttestationFlowIntegrated(
::attestation::ACAType aca_type)
: AttestationFlow(/*server_proxy=*/nullptr),
aca_type_(aca_type),
attestation_client_(AttestationClient::Get()),
ready_timeout_(kReadyTimeout),
retry_delay_(kRetryDelay) {}
AttestationFlowIntegrated::~AttestationFlowIntegrated() = default;
void AttestationFlowIntegrated::GetCertificate(
AttestationCertificateProfile certificate_profile,
const AccountId& account_id,
const std::string& request_origin,
bool force_new_key,
const std::string& key_name,
CertificateCallback callback) {
const std::string attestation_key_name =
!key_name.empty()
? key_name
: GetKeyNameForProfile(certificate_profile, request_origin);
base::OnceCallback<void(bool)> start_certificate_request = base::BindOnce(
&AttestationFlowIntegrated::StartCertificateRequest,
weak_factory_.GetWeakPtr(), certificate_profile, account_id,
request_origin, force_new_key, attestation_key_name, std::move(callback));
base::TimeTicks end_time = base::TimeTicks::Now() + ready_timeout_;
WaitForAttestationPrepared(end_time, std::move(start_certificate_request));
}
void AttestationFlowIntegrated::WaitForAttestationPrepared(
base::TimeTicks end_time,
base::OnceCallback<void(bool)> callback) {
::attestation::GetEnrollmentPreparationsRequest request;
request.set_aca_type(aca_type_);
attestation_client_->GetEnrollmentPreparations(
request, base::BindOnce(
&AttestationFlowIntegrated::OnPreparedCheckComplete,
weak_factory_.GetWeakPtr(), end_time, std::move(callback)));
}
void AttestationFlowIntegrated::OnPreparedCheckComplete(
base::TimeTicks end_time,
base::OnceCallback<void(bool)> callback,
const ::attestation::GetEnrollmentPreparationsReply& reply) {
if (reply.status() == ::attestation::STATUS_SUCCESS &&
IsPreparedWith(reply, aca_type_)) {
std::move(callback).Run(true);
return;
}
if (base::TimeTicks::Now() < end_time) {
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&AttestationFlowIntegrated::WaitForAttestationPrepared,
weak_factory_.GetWeakPtr(), end_time,
std::move(callback)),
retry_delay_);
return;
}
std::move(callback).Run(false);
}
void AttestationFlowIntegrated::StartCertificateRequest(
AttestationCertificateProfile certificate_profile,
const AccountId& account_id,
const std::string& request_origin,
bool generate_new_key,
const std::string& key_name,
CertificateCallback callback,
bool is_prepared) {
if (!is_prepared) {
LOG(ERROR) << __func__ << ": Not prepared.";
std::move(callback).Run(ATTESTATION_UNSPECIFIED_FAILURE, "");
return;
}
::attestation::GetCertificateRequest request;
request.set_aca_type(aca_type_);
base::Optional<::attestation::CertificateProfile> profile_attestation_enum =
ProfileToAttestationProtoEnum(certificate_profile);
if (!profile_attestation_enum) {
LOG(ERROR) << __func__ << ": Unexpected profile value: "
<< static_cast<int>(certificate_profile);
std::move(callback).Run(ATTESTATION_UNSPECIFIED_FAILURE, "");
return;
}
request.set_certificate_profile(*profile_attestation_enum);
request.set_request_origin(request_origin);
request.set_username(cryptohome::Identification(account_id).id());
request.set_key_label(key_name);
request.set_shall_trigger_enrollment(true);
request.set_forced(generate_new_key);
attestation_client_->GetCertificate(
request, base::BindOnce(&AttestationFlowIntegrated::OnCertRequestFinished,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
void AttestationFlowIntegrated::OnCertRequestFinished(
CertificateCallback callback,
const ::attestation::GetCertificateReply& reply) {
if (reply.status() == ::attestation::STATUS_SUCCESS) {
std::move(callback).Run(ATTESTATION_SUCCESS, reply.certificate());
} else {
std::move(callback).Run(ATTESTATION_UNSPECIFIED_FAILURE, "");
}
}
} // namespace attestation
} // namespace chromeos