Index: chromeos/attestation/attestation_flow.cc |
diff --git a/chromeos/attestation/attestation_flow.cc b/chromeos/attestation/attestation_flow.cc |
index c82cbe554008bf6f81cde6267570743de5e8c3b5..d8d8145d14b6b87406ddf9ae16ea68add1103ec2 100644 |
--- a/chromeos/attestation/attestation_flow.cc |
+++ b/chromeos/attestation/attestation_flow.cc |
@@ -7,6 +7,8 @@ |
#include <utility> |
#include "base/bind.h" |
+#include "base/memory/ptr_util.h" |
+#include "base/timer/timer.h" |
#include "chromeos/cryptohome/async_method_caller.h" |
#include "chromeos/cryptohome/cryptohome_parameters.h" |
#include "chromeos/dbus/cryptohome_client.h" |
@@ -17,6 +19,33 @@ namespace attestation { |
namespace { |
+// Default backoff policy. |
+const net::BackoffEntry::Policy kDefaultBackoffPolicy = { |
+ // Number of initial errors (in sequence) to ignore before applying |
+ // exponential back-off rules. |
+ 0, |
+ |
+ // Initial delay for exponential back-off in ms. |
+ 2000, |
+ |
+ // Factor by which the waiting time will be multiplied. |
+ 2, |
+ |
+ // Fuzzing percentage. ex: 10% will spread requests randomly |
+ // between 90%-100% of the calculated time. |
+ 0, |
+ |
+ // Maximum amount of time we are willing to delay our request in ms. |
+ -1, |
+ |
+ // Time to keep an entry from being discarded even when it |
+ // has no significant state, -1 to never discard. |
+ -1, |
+ |
+ // Don't use initial delay unless the last request was an error. |
+ false, |
+}; |
+ |
// Redirects to one of three callbacks based on a boolean value and dbus call |
// status. |
// |
@@ -29,10 +58,11 @@ namespace { |
void DBusBoolRedirectCallback(const base::Closure& on_true, |
const base::Closure& on_false, |
const base::Closure& on_fail, |
+ const std::string& on_fail_message, |
DBusMethodCallStatus status, |
bool value) { |
if (status != DBUS_METHOD_CALL_SUCCESS) { |
- LOG(ERROR) << "Attestation: Failed to query enrollment state."; |
+ LOG(ERROR) << "Attestation: Failed to " << on_fail_message << "."; |
if (!on_fail.is_null()) |
on_fail.Run(); |
return; |
@@ -95,6 +125,8 @@ AttestationFlow::AttestationFlow(cryptohome::AsyncMethodCaller* async_caller, |
: async_caller_(async_caller), |
cryptohome_client_(cryptohome_client), |
server_proxy_(std::move(server_proxy)), |
+ retry_timer_(new base::OneShotTimer()), |
+ retry_backoff_(&kDefaultBackoffPolicy), |
weak_factory_(this) {} |
AttestationFlow::~AttestationFlow() { |
@@ -106,25 +138,71 @@ void AttestationFlow::GetCertificate( |
const std::string& request_origin, |
bool force_new_key, |
const CertificateCallback& callback) { |
+ // Fail requests if we are backing off right now. |
+ if (retry_backoff_.ShouldRejectRequest()) { |
The one and only Dr. Crash
2016/12/02 05:57:49
I actually think that the behavior I want is to st
|
+ if (!callback.is_null()) |
+ callback.Run(false, ""); |
+ return; |
+ } |
// If this device has not enrolled with the Privacy CA, we need to do that |
// first. Once enrolled we can proceed with the certificate request. |
base::Closure do_cert_request = base::Bind( |
&AttestationFlow::StartCertificateRequest, weak_factory_.GetWeakPtr(), |
certificate_profile, account_id, request_origin, force_new_key, callback); |
base::Closure on_enroll_failure = base::Bind(callback, false, ""); |
- base::Closure do_enroll = base::Bind(&AttestationFlow::StartEnroll, |
- weak_factory_.GetWeakPtr(), |
- on_enroll_failure, |
- do_cert_request); |
- cryptohome_client_->TpmAttestationIsEnrolled(base::Bind( |
- &DBusBoolRedirectCallback, |
- do_cert_request, // If enrolled, proceed with cert request. |
- do_enroll, // If not enrolled, initiate enrollment. |
- on_enroll_failure)); |
+ base::Closure initiate_enroll = |
+ base::Bind(&AttestationFlow::InitiateEnroll, weak_factory_.GetWeakPtr(), |
+ on_enroll_failure, do_cert_request); |
+ cryptohome_client_->TpmAttestationIsEnrolled( |
+ base::Bind(&DBusBoolRedirectCallback, |
+ do_cert_request, // If enrolled, proceed with cert request. |
+ initiate_enroll, // If not enrolled, initiate enrollment. |
+ on_enroll_failure, "check enrollment state")); |
+} |
+ |
+void AttestationFlow::SetRetryTimerForTest( |
+ std::unique_ptr<base::OneShotTimer> retry_timer) { |
+ retry_timer_ = std::move(retry_timer); |
+} |
+ |
+void AttestationFlow::InitiateEnroll(const base::Closure& on_failure, |
+ const base::Closure& next_task) { |
+ base::Closure do_enroll = |
+ base::Bind(&AttestationFlow::StartEnroll, weak_factory_.GetWeakPtr(), |
+ on_failure, next_task); |
+ base::Closure retry_initiate_enroll = |
+ base::Bind(&AttestationFlow::RetryInitiateEnroll, |
+ weak_factory_.GetWeakPtr(), on_failure, next_task); |
+ cryptohome_client_->TpmAttestationIsPrepared( |
+ base::Bind(&DBusBoolRedirectCallback, do_enroll, retry_initiate_enroll, |
+ on_failure, "check for attestation readiness")); |
+} |
+ |
+void AttestationFlow::RetryInitiateEnroll(const base::Closure& on_failure, |
+ const base::Closure& next_task) { |
+ if (initiate_enroll_retries_ == 0) { |
+ LOG(ERROR) << "Attestation: not ready for attestation." |
+ << " Giving up on retrying."; |
+ if (!on_failure.is_null()) |
+ on_failure.Run(); |
+ } else { |
+ if (initiate_enroll_retries_ > 0) |
+ --initiate_enroll_retries_; |
+ retry_backoff_.InformOfRequest(false); |
+ auto retry_delay = retry_backoff_.GetTimeUntilRelease().InMilliseconds(); |
+ LOG(WARNING) << "Attestation: not ready for attestation yet." |
+ << " Retrying in " << retry_delay << " ms."; |
+ retry_timer_->Start( |
+ FROM_HERE, retry_backoff_.GetTimeUntilRelease(), |
+ base::Bind(&AttestationFlow::InitiateEnroll, weak_factory_.GetWeakPtr(), |
+ on_failure, next_task)); |
+ } |
} |
void AttestationFlow::StartEnroll(const base::Closure& on_failure, |
const base::Closure& next_task) { |
+ // We are done retrying the enrollment initiation (i.e. TPM is ready). |
+ retry_backoff_.Reset(); |
// Get the attestation service to create a Privacy CA enrollment request. |
async_caller_->AsyncTpmAttestationCreateEnrollRequest( |
server_proxy_->GetType(), |
@@ -222,7 +300,8 @@ void AttestationFlow::StartCertificateRequest( |
cryptohome_client_->TpmAttestationDoesKeyExist( |
key_type, cryptohome::Identification(account_id), key_name, |
base::Bind(&DBusBoolRedirectCallback, on_key_exists, on_key_not_exists, |
- base::Bind(callback, false, ""))); |
+ base::Bind(callback, false, ""), |
+ "check for existence of attestation key")); |
} |
} |