// Copyright (c) 2012 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.h" #include "base/bind.h" #include "chromeos/cryptohome/async_method_caller.h" #include "chromeos/dbus/cryptohome_client.h" namespace chromeos { namespace attestation { namespace { // Redirects to one of three callbacks based on a boolean value and dbus call // status. // // Parameters // on_true - Called when status=succes and value=true. // on_false - Called when status=success and value=false. // on_fail - Called when status=failure. // status - The D-Bus operation status. // value - The value returned by the D-Bus operation. void DBusBoolRedirectCallback(const base::Closure& on_true, const base::Closure& on_false, const base::Closure& on_fail, DBusMethodCallStatus status, bool value) { if (status != DBUS_METHOD_CALL_SUCCESS) { LOG(ERROR) << "Attestation: Failed to query enrollment state."; if (!on_fail.is_null()) on_fail.Run(); return; } const base::Closure& task = value ? on_true : on_false; if (!task.is_null()) task.Run(); } void DBusDataMethodCallback( const AttestationFlow::CertificateCallback& callback, DBusMethodCallStatus status, bool result, const std::string& data) { if (status != DBUS_METHOD_CALL_SUCCESS) { LOG(ERROR) << "Attestation: DBus data operation failed."; if (!callback.is_null()) callback.Run(false, ""); return; } if (!callback.is_null()) callback.Run(result, data); } AttestationKeyType GetKeyTypeForProfile( AttestationCertificateProfile profile) { switch (profile) { case PROFILE_ENTERPRISE_MACHINE_CERTIFICATE: return KEY_DEVICE; case PROFILE_ENTERPRISE_USER_CERTIFICATE: return KEY_USER; } NOTREACHED(); return KEY_USER; } std::string GetKeyNameForProfile( AttestationCertificateProfile profile) { switch (profile) { case PROFILE_ENTERPRISE_MACHINE_CERTIFICATE: return kEnterpriseMachineKey; case PROFILE_ENTERPRISE_USER_CERTIFICATE: return kEnterpriseUserKey; } NOTREACHED(); return ""; } int GetCertificateOptionsForProfile( AttestationCertificateProfile profile) { switch (profile) { case PROFILE_ENTERPRISE_MACHINE_CERTIFICATE: return CERTIFICATE_INCLUDE_STABLE_ID | CERTIFICATE_INCLUDE_DEVICE_STATE; case PROFILE_ENTERPRISE_USER_CERTIFICATE: return CERTIFICATE_INCLUDE_DEVICE_STATE; } NOTREACHED(); return CERTIFICATE_OPTION_NONE; } } // namespace AttestationFlow::AttestationFlow(cryptohome::AsyncMethodCaller* async_caller, CryptohomeClient* cryptohome_client, scoped_ptr server_proxy) : async_caller_(async_caller), cryptohome_client_(cryptohome_client), server_proxy_(server_proxy.Pass()), weak_factory_(this) { } AttestationFlow::~AttestationFlow() { } void AttestationFlow::GetCertificate( AttestationCertificateProfile certificate_profile, bool force_new_key, const CertificateCallback& callback) { // 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, 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)); } void AttestationFlow::StartEnroll(const base::Closure& on_failure, const base::Closure& next_task) { // Get the attestation service to create a Privacy CA enrollment request. async_caller_->AsyncTpmAttestationCreateEnrollRequest(base::Bind( &AttestationFlow::SendEnrollRequestToPCA, weak_factory_.GetWeakPtr(), on_failure, next_task)); } void AttestationFlow::SendEnrollRequestToPCA(const base::Closure& on_failure, const base::Closure& next_task, bool success, const std::string& data) { if (!success) { LOG(ERROR) << "Attestation: Failed to create enroll request."; if (!on_failure.is_null()) on_failure.Run(); return; } // Send the request to the Privacy CA. server_proxy_->SendEnrollRequest( data, base::Bind(&AttestationFlow::SendEnrollResponseToDaemon, weak_factory_.GetWeakPtr(), on_failure, next_task)); } void AttestationFlow::SendEnrollResponseToDaemon( const base::Closure& on_failure, const base::Closure& next_task, bool success, const std::string& data) { if (!success) { LOG(ERROR) << "Attestation: Enroll request failed."; if (!on_failure.is_null()) on_failure.Run(); return; } // Forward the response to the attestation service to complete enrollment. async_caller_->AsyncTpmAttestationEnroll( data, base::Bind(&AttestationFlow::OnEnrollComplete, weak_factory_.GetWeakPtr(), on_failure, next_task)); } void AttestationFlow::OnEnrollComplete(const base::Closure& on_failure, const base::Closure& next_task, bool success, cryptohome::MountError /*not_used*/) { if (!success) { LOG(ERROR) << "Attestation: Failed to complete enrollment."; if (!on_failure.is_null()) on_failure.Run(); return; } // Enrollment has successfully completed, we can move on to whatever is next. if (!next_task.is_null()) next_task.Run(); } void AttestationFlow::StartCertificateRequest( AttestationCertificateProfile certificate_profile, bool generate_new_key, const CertificateCallback& callback) { AttestationKeyType key_type = GetKeyTypeForProfile(certificate_profile); std::string key_name = GetKeyNameForProfile(certificate_profile); if (generate_new_key) { // Get the attestation service to create a Privacy CA certificate request. async_caller_->AsyncTpmAttestationCreateCertRequest( GetCertificateOptionsForProfile(certificate_profile), base::Bind(&AttestationFlow::SendCertificateRequestToPCA, weak_factory_.GetWeakPtr(), key_type, key_name, callback)); } else { // If the key already exists, query the existing certificate. base::Closure on_key_exists = base::Bind( &AttestationFlow::GetExistingCertificate, weak_factory_.GetWeakPtr(), key_type, key_name, callback); // If the key does not exist, call this method back with |generate_new_key| // set to true. base::Closure on_key_not_exists = base::Bind( &AttestationFlow::StartCertificateRequest, weak_factory_.GetWeakPtr(), certificate_profile, true, callback); cryptohome_client_->TpmAttestationDoesKeyExist( key_type, key_name, base::Bind(&DBusBoolRedirectCallback, on_key_exists, on_key_not_exists, base::Bind(callback, false, ""))); } } void AttestationFlow::SendCertificateRequestToPCA( AttestationKeyType key_type, const std::string& key_name, const CertificateCallback& callback, bool success, const std::string& data) { if (!success) { LOG(ERROR) << "Attestation: Failed to create certificate request."; if (!callback.is_null()) callback.Run(false, ""); return; } // Send the request to the Privacy CA. server_proxy_->SendCertificateRequest( data, base::Bind(&AttestationFlow::SendCertificateResponseToDaemon, weak_factory_.GetWeakPtr(), key_type, key_name, callback)); } void AttestationFlow::SendCertificateResponseToDaemon( AttestationKeyType key_type, const std::string& key_name, const CertificateCallback& callback, bool success, const std::string& data) { if (!success) { LOG(ERROR) << "Attestation: Certificate request failed."; if (!callback.is_null()) callback.Run(false, ""); return; } // Forward the response to the attestation service to complete the operation. async_caller_->AsyncTpmAttestationFinishCertRequest(data, key_type, key_name, base::Bind(callback)); } void AttestationFlow::GetExistingCertificate( AttestationKeyType key_type, const std::string& key_name, const CertificateCallback& callback) { cryptohome_client_->TpmAttestationGetCertificate( key_type, key_name, base::Bind(&DBusDataMethodCallback, callback)); } } // namespace attestation } // namespace chromeos