diff options
author | dkrahn@google.com <dkrahn@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-06 16:42:29 +0000 |
---|---|---|
committer | dkrahn@google.com <dkrahn@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-06 16:42:29 +0000 |
commit | 96f72d77c9d7d894481074f2742d5f2cdc4e04c7 (patch) | |
tree | a9e9e0811b9e890be1732e68ac9078a48aec3589 /chromeos | |
parent | 5e02229f7f2a22644feec7b1948e2816b5788b3f (diff) | |
download | chromium_src-96f72d77c9d7d894481074f2742d5f2cdc4e04c7.zip chromium_src-96f72d77c9d7d894481074f2742d5f2cdc4e04c7.tar.gz chromium_src-96f72d77c9d7d894481074f2742d5f2cdc4e04c7.tar.bz2 |
Implemented attestation message flow for Chrome OS.
Chrome OS has an attestation service which can interact with a Privacy CA in order to generate an attestation certificate. The AttestationFlow class coordinates this interaction.
BUG=chromium-os:37806
TEST=unit
Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=178354
Review URL: https://chromiumcodereview.appspot.com/11932004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@180987 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chromeos')
-rw-r--r-- | chromeos/attestation/attestation_flow.cc | 192 | ||||
-rw-r--r-- | chromeos/attestation/attestation_flow.h | 172 | ||||
-rw-r--r-- | chromeos/attestation/attestation_flow_unittest.cc | 311 | ||||
-rw-r--r-- | chromeos/attestation/mock_attestation_flow.cc | 46 | ||||
-rw-r--r-- | chromeos/attestation/mock_attestation_flow.h | 61 | ||||
-rw-r--r-- | chromeos/chromeos.gyp | 5 | ||||
-rw-r--r-- | chromeos/cryptohome/mock_async_method_caller.cc | 34 | ||||
-rw-r--r-- | chromeos/cryptohome/mock_async_method_caller.h | 8 |
8 files changed, 829 insertions, 0 deletions
diff --git a/chromeos/attestation/attestation_flow.cc b/chromeos/attestation/attestation_flow.cc new file mode 100644 index 0000000..b60e194 --- /dev/null +++ b/chromeos/attestation/attestation_flow.cc @@ -0,0 +1,192 @@ +// 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(); +} + +} // namespace + +const char AttestationFlow::kEnterpriseMachineKey[] = "attest-ent-machine"; + +AttestationFlow::AttestationFlow(cryptohome::AsyncMethodCaller* async_caller, + CryptohomeClient* cryptohome_client, + ServerProxy* server_proxy) + : ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), + async_caller_(async_caller), + cryptohome_client_(cryptohome_client), + server_proxy_(server_proxy) { +} + +AttestationFlow::~AttestationFlow() { +} + +void AttestationFlow::GetCertificate(const std::string& name, + 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(), + name, + 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( + const std::string& name, + const CertificateCallback& callback) { + // Get the attestation service to create a Privacy CA certificate request. + async_caller_->AsyncTpmAttestationCreateCertRequest( + (name == kEnterpriseMachineKey), + base::Bind(&AttestationFlow::SendCertificateRequestToPCA, + weak_factory_.GetWeakPtr(), + callback)); +} + +void AttestationFlow::SendCertificateRequestToPCA( + 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(), + callback)); +} + +void AttestationFlow::SendCertificateResponseToDaemon( + 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, + base::Bind(callback)); +} + +} // namespace attestation +} // namespace chromeos diff --git a/chromeos/attestation/attestation_flow.h b/chromeos/attestation/attestation_flow.h new file mode 100644 index 0000000..8e7bb18 --- /dev/null +++ b/chromeos/attestation/attestation_flow.h @@ -0,0 +1,172 @@ +// 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. + +#ifndef CHROMEOS_ATTESTATION_ATTESTATION_FLOW_H_ +#define CHROMEOS_ATTESTATION_ATTESTATION_FLOW_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/callback_forward.h" +#include "base/memory/weak_ptr.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/dbus_method_call_status.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace cryptohome { + +class AsyncMethodCaller; + +} // namespace cryptohome + +namespace chromeos { + +class CryptohomeClient; + +namespace attestation { + +// Interface for access to the Privacy CA server. +class CHROMEOS_EXPORT ServerProxy { + public: + typedef base::Callback<void(bool success, + const std::string& data)> DataCallback; + virtual ~ServerProxy() {} + virtual void SendEnrollRequest(const std::string& request, + const DataCallback& on_response) = 0; + virtual void SendCertificateRequest(const std::string& request, + const DataCallback& on_response) = 0; +}; + +// Implements the message flow for Chrome OS attestation tasks. Generally this +// consists of coordinating messages between the Chrome OS attestation service +// and the Privacy CA server. Sample usage: +// AttestationFlow flow(AsyncMethodCaller::GetInstance(), +// DBusThreadManager::Get().GetCryptohomeClient(), +// my_server_proxy); +// CertificateCallback callback = base::Bind(&MyCallback); +// flow.GetCertificate("attest-ent-machine", callback); +class CHROMEOS_EXPORT AttestationFlow { + public: + typedef base::Callback<void(bool success, + const std::string& pem_certificate_chain)> + CertificateCallback; + + AttestationFlow(cryptohome::AsyncMethodCaller* async_caller, + CryptohomeClient* cryptohome_client, + ServerProxy* server_proxy); + virtual ~AttestationFlow(); + + // Asynchronously gets an attestation certificate bound to the given name. + // If no certificate has been associated with the name, a new certificate is + // issued. + // + // Parameters + // name - The name of the key for which to retrieve a certificate. The + // following key names are available: + // "attest-ent-machine" - The enterprise machine key. + // "attest-ent-user" - An enterprise user key for the current user. + // "content-[origin]" - A content protection key bound to a + // specific origin for the current user. + // callback - A callback which will be called when the operation completes. + virtual void GetCertificate(const std::string& name, + const CertificateCallback& callback); + + private: + // The key name defined for the special-purpose Enterprise Machine Key. + static const char kEnterpriseMachineKey[]; + + // Asynchronously initiates the attestation enrollment flow. + // + // Parameters + // on_failure - Called if any failure occurs. + // next_task - Called on successful enrollment. + void StartEnroll(const base::Closure& on_failure, + const base::Closure& next_task); + + // Called when the attestation daemon has finished creating an enrollment + // request for the Privacy CA. The request is asynchronously forwarded as-is + // to the PCA. + // + // Parameters + // on_failure - Called if any failure occurs. + // next_task - Called on successful enrollment. + // success - The status of request creation. + // data - The request data for the Privacy CA. + void SendEnrollRequestToPCA(const base::Closure& on_failure, + const base::Closure& next_task, + bool success, + const std::string& data); + + // Called when the Privacy CA responds to an enrollment request. The response + // is asynchronously forwarded as-is to the attestation daemon in order to + // complete the enrollment operation. + // + // Parameters + // on_failure - Called if any failure occurs. + // next_task - Called on successful enrollment. + // success - The status of the Privacy CA operation. + // data - The response data from the Privacy CA. + void SendEnrollResponseToDaemon(const base::Closure& on_failure, + const base::Closure& next_task, + bool success, + const std::string& data); + + // Called when the attestation daemon completes an enrollment operation. If + // the operation was successful, the next_task callback is called. + // + // Parameters + // on_failure - Called if any failure occurs. + // next_task - Called on successful enrollment. + // success - The status of the enrollment operation. + // not_used - An artifact of the cryptohome D-Bus interface; ignored. + void OnEnrollComplete(const base::Closure& on_failure, + const base::Closure& next_task, + bool success, + cryptohome::MountError not_used); + + // Asynchronously initiates the certificate request flow. Attestation + // enrollment must complete successfully before this operation can succeed. + // + // Parameters + // name - The name of the key for which a certificate is requested. + // callback - Called when the operation completes. + void StartCertificateRequest(const std::string& name, + const CertificateCallback& callback); + + // Called when the attestation daemon has finished creating a certificate + // request for the Privacy CA. The request is asynchronously forwarded as-is + // to the PCA. + // + // Parameters + // callback - Called when the operation completes. + // success - The status of request creation. + // data - The request data for the Privacy CA. + void SendCertificateRequestToPCA(const CertificateCallback& callback, + bool success, + const std::string& data); + + // Called when the Privacy CA responds to a certificate request. The response + // is asynchronously forwarded as-is to the attestation daemon in order to + // complete the operation. + // + // Parameters + // callback - Called when the operation completes. + // success - The status of the Privacy CA operation. + // data - The response data from the Privacy CA. + void SendCertificateResponseToDaemon(const CertificateCallback& callback, + bool success, + const std::string& data); + + base::WeakPtrFactory<AttestationFlow> weak_factory_; + cryptohome::AsyncMethodCaller* async_caller_; + CryptohomeClient* cryptohome_client_; + ServerProxy* server_proxy_; + + DISALLOW_COPY_AND_ASSIGN(AttestationFlow); +}; + +} // namespace attestation +} // namespace chromeos + +#endif // CHROMEOS_ATTESTATION_ATTESTATION_FLOW_H_ diff --git a/chromeos/attestation/attestation_flow_unittest.cc b/chromeos/attestation/attestation_flow_unittest.cc new file mode 100644 index 0000000..51410e2 --- /dev/null +++ b/chromeos/attestation/attestation_flow_unittest.cc @@ -0,0 +1,311 @@ +// 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 "base/bind.h" +#include "base/run_loop.h" +#include "chromeos/attestation/mock_attestation_flow.h" +#include "chromeos/cryptohome/mock_async_method_caller.h" +#include "chromeos/dbus/mock_cryptohome_client.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::Invoke; +using testing::Sequence; +using testing::StrictMock; +using testing::WithArgs; + +namespace chromeos { +namespace attestation { + +namespace { + +void DBusCallbackFalse(const BoolDBusMethodCallback& callback) { + MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, false)); +} + +void DBusCallbackTrue(const BoolDBusMethodCallback& callback) { + MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, true)); +} + +void DBusCallbackFail(const BoolDBusMethodCallback& callback) { + MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_FAILURE, false)); +} + +void AsyncCallbackFalse(cryptohome::AsyncMethodCaller::Callback callback) { + callback.Run(false, cryptohome::MOUNT_ERROR_NONE); +} + +} // namespace + +class AttestationFlowTest : public testing::Test { + protected: + void Run() { + base::RunLoop run_loop; + run_loop.RunUntilIdle(); + } + MessageLoop message_loop_; +}; + +TEST_F(AttestationFlowTest, GetCertificate) { + // Verify the order of calls in a sequence. + Sequence flow_order; + + // Use DBusCallbackFalse so the full enrollment flow is triggered. + chromeos::MockCryptohomeClient client; + EXPECT_CALL(client, TpmAttestationIsEnrolled(_)) + .InSequence(flow_order) + .WillRepeatedly(Invoke(DBusCallbackFalse)); + + // Use StrictMock when we want to verify invocation frequency. + StrictMock<cryptohome::MockAsyncMethodCaller> async_caller; + async_caller.SetUp(true, cryptohome::MOUNT_ERROR_NONE); + EXPECT_CALL(async_caller, AsyncTpmAttestationCreateEnrollRequest(_)) + .Times(1) + .InSequence(flow_order); + + StrictMock<MockServerProxy> proxy; + proxy.DeferToFake(true); + EXPECT_CALL(proxy, SendEnrollRequest( + cryptohome::MockAsyncMethodCaller::kFakeAttestationEnrollRequest, + _)).Times(1) + .InSequence(flow_order); + + std::string fake_enroll_response = + cryptohome::MockAsyncMethodCaller::kFakeAttestationEnrollRequest; + fake_enroll_response += "_response"; + EXPECT_CALL(async_caller, AsyncTpmAttestationEnroll(fake_enroll_response, _)) + .Times(1) + .InSequence(flow_order); + + EXPECT_CALL(async_caller, AsyncTpmAttestationCreateCertRequest(false, _)) + .Times(1) + .InSequence(flow_order); + + EXPECT_CALL(proxy, SendCertificateRequest( + cryptohome::MockAsyncMethodCaller::kFakeAttestationCertRequest, + _)).Times(1) + .InSequence(flow_order); + + std::string fake_cert_response = + cryptohome::MockAsyncMethodCaller::kFakeAttestationCertRequest; + fake_cert_response += "_response"; + EXPECT_CALL(async_caller, + AsyncTpmAttestationFinishCertRequest(fake_cert_response, _)) + .Times(1) + .InSequence(flow_order); + + StrictMock<MockObserver> observer; + EXPECT_CALL(observer, MockCertificateCallback( + true, + cryptohome::MockAsyncMethodCaller::kFakeAttestationCert)) + .Times(1) + .InSequence(flow_order); + AttestationFlow::CertificateCallback mock_callback = base::Bind( + &MockObserver::MockCertificateCallback, + base::Unretained(&observer)); + + AttestationFlow flow(&async_caller, &client, &proxy); + flow.GetCertificate("test", mock_callback); + Run(); +} + +TEST_F(AttestationFlowTest, GetCertificate_NoEK) { + StrictMock<cryptohome::MockAsyncMethodCaller> async_caller; + async_caller.SetUp(false, cryptohome::MOUNT_ERROR_NONE); + EXPECT_CALL(async_caller, AsyncTpmAttestationCreateEnrollRequest(_)) + .Times(1); + + chromeos::MockCryptohomeClient client; + EXPECT_CALL(client, TpmAttestationIsEnrolled(_)) + .WillRepeatedly(Invoke(DBusCallbackFalse)); + + // We're not expecting any server calls in this case; StrictMock will verify. + StrictMock<MockServerProxy> proxy; + + StrictMock<MockObserver> observer; + EXPECT_CALL(observer, MockCertificateCallback(false, "")) + .Times(1); + AttestationFlow::CertificateCallback mock_callback = base::Bind( + &MockObserver::MockCertificateCallback, + base::Unretained(&observer)); + + AttestationFlow flow(&async_caller, &client, &proxy); + flow.GetCertificate("test", mock_callback); + Run(); +} + +TEST_F(AttestationFlowTest, GetCertificate_EKRejected) { + StrictMock<cryptohome::MockAsyncMethodCaller> async_caller; + async_caller.SetUp(true, cryptohome::MOUNT_ERROR_NONE); + EXPECT_CALL(async_caller, AsyncTpmAttestationCreateEnrollRequest(_)) + .Times(1); + + chromeos::MockCryptohomeClient client; + EXPECT_CALL(client, TpmAttestationIsEnrolled(_)) + .WillRepeatedly(Invoke(DBusCallbackFalse)); + + StrictMock<MockServerProxy> proxy; + proxy.DeferToFake(false); + EXPECT_CALL(proxy, SendEnrollRequest( + cryptohome::MockAsyncMethodCaller::kFakeAttestationEnrollRequest, + _)).Times(1); + + StrictMock<MockObserver> observer; + EXPECT_CALL(observer, MockCertificateCallback(false, "")) + .Times(1); + AttestationFlow::CertificateCallback mock_callback = base::Bind( + &MockObserver::MockCertificateCallback, + base::Unretained(&observer)); + + AttestationFlow flow(&async_caller, &client, &proxy); + flow.GetCertificate("test", mock_callback); + Run(); +} + +TEST_F(AttestationFlowTest, GetCertificate_FailEnroll) { + StrictMock<cryptohome::MockAsyncMethodCaller> async_caller; + async_caller.SetUp(true, cryptohome::MOUNT_ERROR_NONE); + EXPECT_CALL(async_caller, AsyncTpmAttestationCreateEnrollRequest(_)) + .Times(1); + std::string fake_enroll_response = + cryptohome::MockAsyncMethodCaller::kFakeAttestationEnrollRequest; + fake_enroll_response += "_response"; + EXPECT_CALL(async_caller, AsyncTpmAttestationEnroll(fake_enroll_response, _)) + .WillOnce(WithArgs<1>(Invoke(AsyncCallbackFalse))); + + chromeos::MockCryptohomeClient client; + EXPECT_CALL(client, TpmAttestationIsEnrolled(_)) + .WillRepeatedly(Invoke(DBusCallbackFalse)); + + StrictMock<MockServerProxy> proxy; + proxy.DeferToFake(true); + EXPECT_CALL(proxy, SendEnrollRequest( + cryptohome::MockAsyncMethodCaller::kFakeAttestationEnrollRequest, + _)).Times(1); + + StrictMock<MockObserver> observer; + EXPECT_CALL(observer, MockCertificateCallback(false, "")).Times(1); + AttestationFlow::CertificateCallback mock_callback = base::Bind( + &MockObserver::MockCertificateCallback, + base::Unretained(&observer)); + + AttestationFlow flow(&async_caller, &client, &proxy); + flow.GetCertificate("test", mock_callback); + Run(); +} + +TEST_F(AttestationFlowTest, GetOwnerCertificateAlreadyEnrolled) { + StrictMock<cryptohome::MockAsyncMethodCaller> async_caller; + async_caller.SetUp(true, cryptohome::MOUNT_ERROR_NONE); + EXPECT_CALL(async_caller, AsyncTpmAttestationCreateCertRequest(true, _)) + .Times(1); + std::string fake_cert_response = + cryptohome::MockAsyncMethodCaller::kFakeAttestationCertRequest; + fake_cert_response += "_response"; + EXPECT_CALL(async_caller, + AsyncTpmAttestationFinishCertRequest(fake_cert_response, _)) + .Times(1); + + chromeos::MockCryptohomeClient client; + EXPECT_CALL(client, TpmAttestationIsEnrolled(_)) + .WillRepeatedly(Invoke(DBusCallbackTrue)); + + StrictMock<MockServerProxy> proxy; + proxy.DeferToFake(true); + EXPECT_CALL(proxy, SendCertificateRequest( + cryptohome::MockAsyncMethodCaller::kFakeAttestationCertRequest, + _)).Times(1); + + StrictMock<MockObserver> observer; + EXPECT_CALL(observer, MockCertificateCallback( + true, + cryptohome::MockAsyncMethodCaller::kFakeAttestationCert)).Times(1); + AttestationFlow::CertificateCallback mock_callback = base::Bind( + &MockObserver::MockCertificateCallback, + base::Unretained(&observer)); + + AttestationFlow flow(&async_caller, &client, &proxy); + flow.GetCertificate("attest-ent-machine", mock_callback); + Run(); +} + +TEST_F(AttestationFlowTest, GetCertificate_FailCreateCertRequest) { + StrictMock<cryptohome::MockAsyncMethodCaller> async_caller; + async_caller.SetUp(false, cryptohome::MOUNT_ERROR_NONE); + EXPECT_CALL(async_caller, AsyncTpmAttestationCreateCertRequest(false, _)) + .Times(1); + + chromeos::MockCryptohomeClient client; + EXPECT_CALL(client, TpmAttestationIsEnrolled(_)) + .WillRepeatedly(Invoke(DBusCallbackTrue)); + + // We're not expecting any server calls in this case; StrictMock will verify. + StrictMock<MockServerProxy> proxy; + + StrictMock<MockObserver> observer; + EXPECT_CALL(observer, MockCertificateCallback(false, "")).Times(1); + AttestationFlow::CertificateCallback mock_callback = base::Bind( + &MockObserver::MockCertificateCallback, + base::Unretained(&observer)); + + AttestationFlow flow(&async_caller, &client, &proxy); + flow.GetCertificate("test", mock_callback); + Run(); +} + +TEST_F(AttestationFlowTest, GetCertificate_CertRequestRejected) { + StrictMock<cryptohome::MockAsyncMethodCaller> async_caller; + async_caller.SetUp(true, cryptohome::MOUNT_ERROR_NONE); + EXPECT_CALL(async_caller, AsyncTpmAttestationCreateCertRequest(false, _)) + .Times(1); + + chromeos::MockCryptohomeClient client; + EXPECT_CALL(client, TpmAttestationIsEnrolled(_)) + .WillRepeatedly(Invoke(DBusCallbackTrue)); + + StrictMock<MockServerProxy> proxy; + proxy.DeferToFake(false); + EXPECT_CALL(proxy, SendCertificateRequest( + cryptohome::MockAsyncMethodCaller::kFakeAttestationCertRequest, + _)).Times(1); + + StrictMock<MockObserver> observer; + EXPECT_CALL(observer, MockCertificateCallback(false, "")).Times(1); + AttestationFlow::CertificateCallback mock_callback = base::Bind( + &MockObserver::MockCertificateCallback, + base::Unretained(&observer)); + + AttestationFlow flow(&async_caller, &client, &proxy); + flow.GetCertificate("test", mock_callback); + Run(); +} + +TEST_F(AttestationFlowTest, GetCertificate_FailIsEnrolled) { + // We're not expecting any server calls in this case; StrictMock will verify. + StrictMock<cryptohome::MockAsyncMethodCaller> async_caller; + + chromeos::MockCryptohomeClient client; + EXPECT_CALL(client, TpmAttestationIsEnrolled(_)) + .WillRepeatedly(Invoke(DBusCallbackFail)); + + // We're not expecting any server calls in this case; StrictMock will verify. + StrictMock<MockServerProxy> proxy; + + StrictMock<MockObserver> observer; + EXPECT_CALL(observer, MockCertificateCallback(false, "")).Times(1); + AttestationFlow::CertificateCallback mock_callback = base::Bind( + &MockObserver::MockCertificateCallback, + base::Unretained(&observer)); + + AttestationFlow flow(&async_caller, &client, &proxy); + flow.GetCertificate("test", mock_callback); + Run(); +} + +} // namespace attestation +} // namespace chromeos diff --git a/chromeos/attestation/mock_attestation_flow.cc b/chromeos/attestation/mock_attestation_flow.cc new file mode 100644 index 0000000..71de13e --- /dev/null +++ b/chromeos/attestation/mock_attestation_flow.cc @@ -0,0 +1,46 @@ +// 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/mock_attestation_flow.h" + +#include "testing/gmock/include/gmock/gmock.h" + +using testing::_; +using testing::Invoke; + +namespace chromeos { +namespace attestation { + +FakeServerProxy::FakeServerProxy() : result_(true) {} + +FakeServerProxy::~FakeServerProxy() {} + +void FakeServerProxy::SendEnrollRequest(const std::string& request, + const DataCallback& callback) { + callback.Run(result_, request + "_response"); +} + +void FakeServerProxy::SendCertificateRequest(const std::string& request, + const DataCallback& callback) { + callback.Run(result_, request + "_response"); +} + +MockServerProxy::MockServerProxy() {} + +MockServerProxy::~MockServerProxy() {} + +void MockServerProxy::DeferToFake(bool success) { + fake_.set_result(success); + ON_CALL(*this, SendEnrollRequest(_, _)) + .WillByDefault(Invoke(&fake_, &FakeServerProxy::SendEnrollRequest)); + ON_CALL(*this, SendCertificateRequest(_, _)) + .WillByDefault(Invoke(&fake_, &FakeServerProxy::SendCertificateRequest)); +} + +MockObserver::MockObserver() {} + +MockObserver::~MockObserver() {} + +} // namespace attestation +} // namespace chromeos diff --git a/chromeos/attestation/mock_attestation_flow.h b/chromeos/attestation/mock_attestation_flow.h new file mode 100644 index 0000000..c9dc9d5 --- /dev/null +++ b/chromeos/attestation/mock_attestation_flow.h @@ -0,0 +1,61 @@ +// 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/basictypes.h" +#include "base/callback.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace chromeos { +namespace attestation { + +// A fake server proxy which just appends "_response" to every request. +class FakeServerProxy : public ServerProxy { + public: + FakeServerProxy(); + virtual ~FakeServerProxy(); + + void set_result(bool result) { + result_ = result; + } + + virtual void SendEnrollRequest(const std::string& request, + const DataCallback& callback) OVERRIDE; + + virtual void SendCertificateRequest(const std::string& request, + const DataCallback& callback) OVERRIDE; + + private: + bool result_; + + DISALLOW_COPY_AND_ASSIGN(FakeServerProxy); +}; + +class MockServerProxy : public ServerProxy { + public: + MockServerProxy(); + virtual ~MockServerProxy(); + + void DeferToFake(bool result); + MOCK_METHOD2(SendEnrollRequest, + void(const std::string&, const DataCallback&)); + MOCK_METHOD2(SendCertificateRequest, + void(const std::string&, const DataCallback&)); + + private: + FakeServerProxy fake_; +}; + +// This class can be used to mock AttestationFlow callbacks. +class MockObserver { + public: + MockObserver(); + virtual ~MockObserver(); + + MOCK_METHOD2(MockCertificateCallback, void(bool, const std::string&)); +}; + +} // namespace attestation +} // namespace chromeos diff --git a/chromeos/chromeos.gyp b/chromeos/chromeos.gyp index a5ac803..ded0c5d 100644 --- a/chromeos/chromeos.gyp +++ b/chromeos/chromeos.gyp @@ -29,6 +29,8 @@ 'CHROMEOS_IMPLEMENTATION', ], 'sources': [ + 'attestation/attestation_flow.cc', + 'attestation/attestation_flow.h', 'chromeos_export.h', 'chromeos_switches.cc', 'chromeos_switches.h', @@ -202,6 +204,8 @@ 'chromeos', ], 'sources': [ + 'attestation/mock_attestation_flow.cc', + 'attestation/mock_attestation_flow.h', 'chromeos_test_utils.cc', 'chromeos_test_utils.h', 'cryptohome/mock_async_method_caller.cc', @@ -308,6 +312,7 @@ 'chromeos_test_support', ], 'sources': [ + 'attestation/attestation_flow_unittest.cc', 'display/output_configurator_unittest.cc', 'dbus/blocking_method_caller_unittest.cc', 'dbus/shill_client_unittest_base.cc', diff --git a/chromeos/cryptohome/mock_async_method_caller.cc b/chromeos/cryptohome/mock_async_method_caller.cc index eb2203c..ce02bd3 100644 --- a/chromeos/cryptohome/mock_async_method_caller.cc +++ b/chromeos/cryptohome/mock_async_method_caller.cc @@ -10,6 +10,10 @@ using ::testing::_; namespace cryptohome { +const char MockAsyncMethodCaller::kFakeAttestationEnrollRequest[] = "enrollreq"; +const char MockAsyncMethodCaller::kFakeAttestationCertRequest[] = "certreq"; +const char MockAsyncMethodCaller::kFakeAttestationCert[] = "cert"; + MockAsyncMethodCaller::MockAsyncMethodCaller() : success_(false), return_code_(cryptohome::MOUNT_ERROR_NONE) { } @@ -34,10 +38,40 @@ void MockAsyncMethodCaller::SetUp(bool success, MountError return_code) { ON_CALL(*this, AsyncRemove(_, _)) .WillByDefault( WithArgs<1>(Invoke(this, &MockAsyncMethodCaller::DoCallback))); + ON_CALL(*this, AsyncTpmAttestationCreateEnrollRequest(_)) + .WillByDefault( + WithArgs<0>(Invoke(this, + &MockAsyncMethodCaller::FakeCreateEnrollRequest))); + ON_CALL(*this, AsyncTpmAttestationEnroll(_, _)) + .WillByDefault( + WithArgs<1>(Invoke(this, &MockAsyncMethodCaller::DoCallback))); + ON_CALL(*this, AsyncTpmAttestationCreateCertRequest(_, _)) + .WillByDefault( + WithArgs<1>(Invoke(this, + &MockAsyncMethodCaller::FakeCreateCertRequest))); + ON_CALL(*this, AsyncTpmAttestationFinishCertRequest(_, _)) + .WillByDefault( + WithArgs<1>(Invoke(this, + &MockAsyncMethodCaller::FakeFinishCertRequest))); } void MockAsyncMethodCaller::DoCallback(Callback callback) { callback.Run(success_, return_code_); } +void MockAsyncMethodCaller::FakeCreateEnrollRequest( + const DataCallback& callback) { + callback.Run(success_, kFakeAttestationEnrollRequest); +} + +void MockAsyncMethodCaller::FakeCreateCertRequest( + const DataCallback& callback) { + callback.Run(success_, kFakeAttestationCertRequest); +} + +void MockAsyncMethodCaller::FakeFinishCertRequest( + const DataCallback& callback) { + callback.Run(success_, kFakeAttestationCert); +} + } // namespace cryptohome diff --git a/chromeos/cryptohome/mock_async_method_caller.h b/chromeos/cryptohome/mock_async_method_caller.h index 37bc0c44..2e8d352 100644 --- a/chromeos/cryptohome/mock_async_method_caller.h +++ b/chromeos/cryptohome/mock_async_method_caller.h @@ -16,6 +16,10 @@ namespace cryptohome { class MockAsyncMethodCaller : public AsyncMethodCaller { public: + static const char kFakeAttestationEnrollRequest[]; + static const char kFakeAttestationCertRequest[]; + static const char kFakeAttestationCert[]; + MockAsyncMethodCaller(); virtual ~MockAsyncMethodCaller(); @@ -50,6 +54,10 @@ class MockAsyncMethodCaller : public AsyncMethodCaller { MountError return_code_; void DoCallback(Callback callback); + // Default fakes for attestation calls. + void FakeCreateEnrollRequest(const DataCallback& callback); + void FakeCreateCertRequest(const DataCallback& callback); + void FakeFinishCertRequest(const DataCallback& callback); DISALLOW_COPY_AND_ASSIGN(MockAsyncMethodCaller); }; |