Commit eda0a0b1 authored by dkrahn@google.com's avatar dkrahn@google.com

Created AttestationPolicyObserver.

    
AttestationPolicyObserver performs Chrome OS attestation work in
response to policy changes.
    
This CL integrates AttestationPolicyObserver with the Chrome OS
device policy infrastructure.  It also defines a AttestationCAClient
skeleton which is necessary to instantiate AttestationFlow and changes
AttestationFlow to take ownership of its ServerProxy instance.
    
BUG=chromium:219959
TEST=unit_tests; chromeos_unittests

Review URL: https://chromiumcodereview.appspot.com/12556004

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@195266 0039d316-1c4b-4281-b951-d872f2087c98
parent 450944ca
...@@ -128,6 +128,10 @@ ...@@ -128,6 +128,10 @@
'chromeos': { 'chromeos': {
'filepath': 'chromeos/', 'filepath': 'chromeos/',
}, },
'chromeos_attestation': {
'filepath': 'chromeos/attestation/|'\
'chrome/browser/chromeos/attestation/',
},
'chromeos_calculator': { 'chromeos_calculator': {
'filepath': 'chrome/common/extensions/docs/examples/apps/calculator/', 'filepath': 'chrome/common/extensions/docs/examples/apps/calculator/',
}, },
...@@ -569,6 +573,7 @@ ...@@ -569,6 +573,7 @@
'chromedriver': ['kkania@chromium.org'], 'chromedriver': ['kkania@chromium.org'],
'chromeos' : ['oshima+watch@chromium.org', 'chromeos' : ['oshima+watch@chromium.org',
'stevenjb+watch@chromium.org'], 'stevenjb+watch@chromium.org'],
'chromeos_attestation' : ['dkrahn+watch@chromium.org'],
'chromeos_calculator' : ['dharcourt@chromium.org'], 'chromeos_calculator' : ['dharcourt@chromium.org'],
'chromeos_net' : ['gauravsh+watch@chromium.org', 'chromeos_net' : ['gauravsh+watch@chromium.org',
'gspencer+watch@chromium.org', 'gspencer+watch@chromium.org',
......
mnissler@chromium.org
pastarmovj@chromium.org
// Copyright (c) 2013 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/attestation/attestation_ca_client.h"
#include <string>
#include "base/bind.h"
#include "base/message_loop.h"
namespace chromeos {
namespace attestation {
AttestationCAClient::AttestationCAClient() {}
AttestationCAClient::~AttestationCAClient() {}
void AttestationCAClient::SendEnrollRequest(const std::string& request,
const DataCallback& on_response) {
NOTIMPLEMENTED();
}
void AttestationCAClient::SendCertificateRequest(
const std::string& request,
const DataCallback& on_response) {
NOTIMPLEMENTED();
}
} // namespace attestation
} // namespace chromeos
// Copyright (c) 2013 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_ATTESTATION_ATTESTATION_CA_CLIENT_H_
#define CHROME_BROWSER_CHROMEOS_ATTESTATION_ATTESTATION_CA_CLIENT_H_
#include <string>
#include "base/basictypes.h"
#include "chromeos/attestation/attestation_flow.h"
namespace chromeos {
namespace attestation {
// This class is a ServerProxy implementation for the Chrome OS attestation
// flow. It sends all requests to an Attestation CA via HTTPS.
class AttestationCAClient : public ServerProxy {
public:
AttestationCAClient();
virtual ~AttestationCAClient();
// chromeos::attestation::ServerProxy:
virtual void SendEnrollRequest(const std::string& request,
const DataCallback& on_response) OVERRIDE;
virtual void SendCertificateRequest(const std::string& request,
const DataCallback& on_response) OVERRIDE;
private:
DISALLOW_COPY_AND_ASSIGN(AttestationCAClient);
};
} // namespace attestation
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_ATTESTATION_ATTESTATION_CA_CLIENT_H_
// Copyright (c) 2013 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/attestation/attestation_policy_observer.h"
#include <string>
#include "base/bind.h"
#include "chrome/browser/chromeos/attestation/attestation_ca_client.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/policy/cloud/cloud_policy_client.h"
#include "chrome/browser/policy/cloud/cloud_policy_manager.h"
#include "chrome/common/chrome_notification_types.h"
#include "chromeos/attestation/attestation_flow.h"
#include "chromeos/cryptohome/async_method_caller.h"
#include "chromeos/dbus/cryptohome_client.h"
#include "chromeos/dbus/dbus_method_call_status.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_details.h"
namespace {
// A dbus callback which handles a boolean result.
//
// Parameters
// on_true - Called when status=success and value=true.
// on_false - Called when status=success and value=false.
// status - The dbus operation status.
// value - The value returned by the dbus operation.
void DBusBoolRedirectCallback(const base::Closure& on_true,
const base::Closure& on_false,
chromeos::DBusMethodCallStatus status,
bool value) {
if (status != chromeos::DBUS_METHOD_CALL_SUCCESS)
return;
const base::Closure& task = value ? on_true : on_false;
if (!task.is_null())
task.Run();
}
// A dbus callback which handles a string result.
//
// Parameters
// on_success - Called when status=success and result=true.
// status - The dbus operation status.
// result - The result returned by the dbus operation.
// data - The data returned by the dbus operation.
void DBusStringCallback(
const base::Callback<void(const std::string&)> on_success,
chromeos::DBusMethodCallStatus status,
bool result,
const std::string& data) {
if (status != chromeos::DBUS_METHOD_CALL_SUCCESS || !result)
return;
on_success.Run(data);
}
} // namespace
namespace chromeos {
namespace attestation {
const char AttestationPolicyObserver::kEnterpriseMachineKey[] =
"attest-ent-machine";
AttestationPolicyObserver::AttestationPolicyObserver(
policy::CloudPolicyClient* policy_client)
: cros_settings_(CrosSettings::Get()),
policy_client_(policy_client),
cryptohome_client_(NULL),
attestation_flow_(NULL),
ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
cros_settings_->AddSettingsObserver(kDeviceAttestationEnabled, this);
Start();
}
AttestationPolicyObserver::AttestationPolicyObserver(
policy::CloudPolicyClient* policy_client,
CryptohomeClient* cryptohome_client,
AttestationFlow* attestation_flow)
: cros_settings_(CrosSettings::Get()),
policy_client_(policy_client),
cryptohome_client_(cryptohome_client),
attestation_flow_(attestation_flow),
ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
cros_settings_->AddSettingsObserver(kDeviceAttestationEnabled, this);
Start();
}
AttestationPolicyObserver::~AttestationPolicyObserver() {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
cros_settings_->RemoveSettingsObserver(kDeviceAttestationEnabled, this);
}
void AttestationPolicyObserver::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
std::string* path = content::Details<std::string>(details).ptr();
if (type != chrome::NOTIFICATION_SYSTEM_SETTING_CHANGED ||
*path != kDeviceAttestationEnabled) {
LOG(WARNING) << "AttestationPolicyObserver: Unexpected event received.";
return;
}
Start();
}
void AttestationPolicyObserver::Start() {
// If attestation is not enabled, there is nothing to do.
bool enabled = false;
if (!cros_settings_->GetBoolean(kDeviceAttestationEnabled, &enabled) ||
!enabled)
return;
// We expect a registered CloudPolicyClient.
if (!policy_client_->is_registered()) {
LOG(ERROR) << "AttestationPolicyObserver: Invalid CloudPolicyClient.";
return;
}
if (!cryptohome_client_)
cryptohome_client_ = DBusThreadManager::Get()->GetCryptohomeClient();
if (!attestation_flow_) {
scoped_ptr<ServerProxy> attestation_ca_client(new AttestationCAClient());
default_attestation_flow_.reset(new AttestationFlow(
cryptohome::AsyncMethodCaller::GetInstance(),
cryptohome_client_,
attestation_ca_client.Pass()));
attestation_flow_ = default_attestation_flow_.get();
}
// Start a dbus call to check if an Enterprise Machine Key already exists.
base::Closure on_does_exist =
base::Bind(&AttestationPolicyObserver::GetExistingCertificate,
weak_factory_.GetWeakPtr());
base::Closure on_does_not_exist =
base::Bind(&AttestationPolicyObserver::GetNewCertificate,
weak_factory_.GetWeakPtr());
cryptohome_client_->TpmAttestationDoesKeyExist(
CryptohomeClient::DEVICE_KEY,
kEnterpriseMachineKey,
base::Bind(DBusBoolRedirectCallback, on_does_exist, on_does_not_exist));
}
void AttestationPolicyObserver::GetNewCertificate() {
// We can reuse the dbus callback handler logic.
attestation_flow_->GetCertificate(
kEnterpriseMachineKey,
base::Bind(DBusStringCallback,
base::Bind(&AttestationPolicyObserver::UploadCertificate,
weak_factory_.GetWeakPtr()),
DBUS_METHOD_CALL_SUCCESS));
}
void AttestationPolicyObserver::GetExistingCertificate() {
cryptohome_client_->TpmAttestationGetCertificate(
CryptohomeClient::DEVICE_KEY,
kEnterpriseMachineKey,
base::Bind(DBusStringCallback,
base::Bind(&AttestationPolicyObserver::CheckCertificateExpiry,
weak_factory_.GetWeakPtr())));
}
void AttestationPolicyObserver::CheckCertificateExpiry(
const std::string& certificate) {
// TODO(dkrahn): Check if the certificate will expire soon, for now assume no.
CheckIfUploaded(certificate);
}
void AttestationPolicyObserver::UploadCertificate(
const std::string& certificate) {
// TODO(dkrahn): Upload the certificate.
}
void AttestationPolicyObserver::CheckIfUploaded(
const std::string& certificate) {
// TODO(dkrahn): Check if we've already uploaded the certificate.
}
} // namespace attestation
} // namespace chromeos
// Copyright (c) 2013 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_ATTESTATION_ATTESTATION_POLICY_OBSERVER_H_
#define CHROME_BROWSER_CHROMEOS_ATTESTATION_ATTESTATION_POLICY_OBSERVER_H_
#include <string>
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "content/public/browser/notification_observer.h"
namespace policy {
class CloudPolicyClient;
}
namespace chromeos {
class CrosSettings;
class CryptohomeClient;
namespace attestation {
class AttestationFlow;
// A class which observes policy changes and triggers device attestation work if
// necessary.
class AttestationPolicyObserver : public content::NotificationObserver {
public:
// The observer immediately connects with CrosSettings to listen for policy
// changes. The CloudPolicyClient is used to upload the device certificate to
// the server if one is created in response to policy changes; it must be in
// the registered state. This class does not take ownership of
// |policy_client|.
explicit AttestationPolicyObserver(policy::CloudPolicyClient* policy_client);
// A constructor which allows custom CryptohomeClient and AttestationFlow
// implementations. Useful for testing.
AttestationPolicyObserver(policy::CloudPolicyClient* policy_client,
CryptohomeClient* cryptohome_client,
AttestationFlow* attestation_flow);
virtual ~AttestationPolicyObserver();
// content::NotificationObserver:
virtual void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) OVERRIDE;
private:
static const char kEnterpriseMachineKey[];
// Checks attestation policy and starts any necessary work.
void Start();
// Gets a new certificate for the Enterprise Machine Key (EMK).
void GetNewCertificate();
// Gets the existing EMK certificate and sends it to CheckCertificateExpiry.
void GetExistingCertificate();
// Checks if the given certificate is expired and, if so, get a new one.
void CheckCertificateExpiry(const std::string& certificate);
// Uploads a certificate to the policy server.
void UploadCertificate(const std::string& certificate);
// Checks if a certificate has already been uploaded and, if not, upload.
void CheckIfUploaded(const std::string& certificate);
CrosSettings* cros_settings_;
policy::CloudPolicyClient* policy_client_;
CryptohomeClient* cryptohome_client_;
AttestationFlow* attestation_flow_;
scoped_ptr<AttestationFlow> default_attestation_flow_;
// Note: This should remain the last member so it'll be destroyed and
// invalidate the weak pointers before any other members are destroyed.
base::WeakPtrFactory<AttestationPolicyObserver> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(AttestationPolicyObserver);
};
} // namespace attestation
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_ATTESTATION_ATTESTATION_POLICY_OBSERVER_H_
// Copyright (c) 2013 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/message_loop.h"
#include "base/run_loop.h"
#include "chrome/browser/chromeos/attestation/attestation_policy_observer.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/chromeos/settings/cros_settings_names.h"
#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h"
#include "chrome/browser/policy/cloud/mock_cloud_policy_client.h"
#include "chromeos/attestation/mock_attestation_flow.h"
#include "chromeos/dbus/mock_cryptohome_client.h"
#include "content/public/test/test_browser_thread.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
using testing::Invoke;
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 DBusDataCallback(const CryptohomeClient::DataMethodCallback& callback) {
MessageLoop::current()->PostTask(
FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, true, "fake"));
}
void CertCallbackSuccess(const AttestationFlow::CertificateCallback& callback) {
MessageLoop::current()->PostTask(
FROM_HERE, base::Bind(callback, true, "fake_cert"));
}
} // namespace
class AttestationPolicyObserverTest : public ::testing::Test {
public:
AttestationPolicyObserverTest()
: message_loop_(MessageLoop::TYPE_UI),
ui_thread_(content::BrowserThread::UI, &message_loop_) {
// Remove the real DeviceSettingsProvider and replace it with a stub.
CrosSettings* cros_settings = CrosSettings::Get();
device_settings_provider_ =
cros_settings->GetProvider(kDeviceAttestationEnabled);
cros_settings->RemoveSettingsProvider(device_settings_provider_);
cros_settings->AddSettingsProvider(&stub_settings_provider_);
cros_settings->SetBoolean(kDeviceAttestationEnabled, true);
policy_client_.SetDMToken("fake_dm_token");
}
virtual ~AttestationPolicyObserverTest() {
// Restore the real DeviceSettingsProvider.
CrosSettings* cros_settings = CrosSettings::Get();
cros_settings->RemoveSettingsProvider(&stub_settings_provider_);
cros_settings->AddSettingsProvider(device_settings_provider_);
}
protected:
void Run() {
AttestationPolicyObserver observer(&policy_client_,
&cryptohome_client_,
&attestation_flow_);
base::RunLoop().RunUntilIdle();
}
MessageLoop message_loop_;
content::TestBrowserThread ui_thread_;
ScopedTestCrosSettings test_cros_settings_;
CrosSettingsProvider* device_settings_provider_;
StubCrosSettingsProvider stub_settings_provider_;
StrictMock<MockCryptohomeClient> cryptohome_client_;
StrictMock<MockAttestationFlow> attestation_flow_;
StrictMock<policy::MockCloudPolicyClient> policy_client_;
};
TEST_F(AttestationPolicyObserverTest, FeatureDisabled) {
CrosSettings* cros_settings = CrosSettings::Get();
cros_settings->SetBoolean(kDeviceAttestationEnabled, false);
Run();
}
TEST_F(AttestationPolicyObserverTest, UnregisteredPolicyClient) {
policy_client_.SetDMToken("");
Run();
}
TEST_F(AttestationPolicyObserverTest, NewCertificate) {
EXPECT_CALL(cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _))
.WillOnce(WithArgs<2>(Invoke(DBusCallbackFalse)));
EXPECT_CALL(attestation_flow_, GetCertificate(_, _))
.WillOnce(WithArgs<1>(Invoke(CertCallbackSuccess)));
Run();
}
TEST_F(AttestationPolicyObserverTest, KeyExists) {
EXPECT_CALL(cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _))
.WillOnce(WithArgs<2>(Invoke(DBusCallbackTrue)));
EXPECT_CALL(cryptohome_client_, TpmAttestationGetCertificate(_, _, _))
.WillOnce(WithArgs<2>(Invoke(DBusDataCallback)));
Run();
}
} // namespace attestation
} // namespace chromeos
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/bind_helpers.h" #include "base/bind_helpers.h"
#include "chrome/browser/chromeos/attestation/attestation_policy_observer.h"
#include "chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h" #include "chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h"
#include "chrome/browser/chromeos/policy/enrollment_handler_chromeos.h" #include "chrome/browser/chromeos/policy/enrollment_handler_chromeos.h"
#include "chrome/browser/chromeos/policy/enterprise_install_attributes.h" #include "chrome/browser/chromeos/policy/enterprise_install_attributes.h"
...@@ -162,6 +163,8 @@ void DeviceCloudPolicyManagerChromeOS::EnrollmentCompleted( ...@@ -162,6 +163,8 @@ void DeviceCloudPolicyManagerChromeOS::EnrollmentCompleted(
core()->StartRefreshScheduler(); core()->StartRefreshScheduler();
core()->TrackRefreshDelayPref(local_state_, core()->TrackRefreshDelayPref(local_state_,
prefs::kDevicePolicyRefreshRate); prefs::kDevicePolicyRefreshRate);
attestation_policy_observer_.reset(
new chromeos::attestation::AttestationPolicyObserver(client()));
} else { } else {
StartIfManaged(); StartIfManaged();
} }
...@@ -181,6 +184,8 @@ void DeviceCloudPolicyManagerChromeOS::StartIfManaged() { ...@@ -181,6 +184,8 @@ void DeviceCloudPolicyManagerChromeOS::StartIfManaged() {
core()->StartRefreshScheduler(); core()->StartRefreshScheduler();
core()->TrackRefreshDelayPref(local_state_, core()->TrackRefreshDelayPref(local_state_,
prefs::kDevicePolicyRefreshRate); prefs::kDevicePolicyRefreshRate);
attestation_policy_observer_.reset(
new chromeos::attestation::AttestationPolicyObserver(client()));
} }
} }
......
...@@ -17,6 +17,12 @@ ...@@ -17,6 +17,12 @@
#include "chrome/browser/policy/cloud/cloud_policy_manager.h" #include "chrome/browser/policy/cloud/cloud_policy_manager.h"
#include "chrome/browser/policy/cloud/cloud_policy_store.h" #include "chrome/browser/policy/cloud/cloud_policy_store.h"
namespace chromeos {
namespace attestation {
class AttestationPolicyObserver;
}
}
class PrefService; class PrefService;
namespace policy { namespace policy {
...@@ -94,6 +100,9 @@ class DeviceCloudPolicyManagerChromeOS : public CloudPolicyManager { ...@@ -94,6 +100,9 @@ class DeviceCloudPolicyManagerChromeOS : public CloudPolicyManager {
// Non-null if there is an enrollment operation pending. // Non-null if there is an enrollment operation pending.
scoped_ptr<EnrollmentHandlerChromeOS> enrollment_handler_; scoped_ptr<EnrollmentHandlerChromeOS> enrollment_handler_;
scoped_ptr<chromeos::attestation::AttestationPolicyObserver>
attestation_policy_observer_;
DISALLOW_COPY_AND_ASSIGN(DeviceCloudPolicyManagerChromeOS); DISALLOW_COPY_AND_ASSIGN(DeviceCloudPolicyManagerChromeOS);
}; };
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h" #include "chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h"
#include "chrome/browser/chromeos/policy/enterprise_install_attributes.h" #include "chrome/browser/chromeos/policy/enterprise_install_attributes.h"
#include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h" #include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/chromeos/settings/device_settings_test_helper.h" #include "chrome/browser/chromeos/settings/device_settings_test_helper.h"
#include "chrome/browser/policy/cloud/cloud_policy_client.h" #include "chrome/browser/policy/cloud/cloud_policy_client.h"
#include "chrome/browser/policy/cloud/mock_device_management_service.h" #include "chrome/browser/policy/cloud/mock_device_management_service.h"
...@@ -74,6 +75,7 @@ class DeviceCloudPolicyManagerChromeOSTest ...@@ -74,6 +75,7 @@ class DeviceCloudPolicyManagerChromeOSTest
TestingPrefServiceSimple local_state_; TestingPrefServiceSimple local_state_;
MockDeviceManagementService device_management_service_; MockDeviceManagementService device_management_service_;
chromeos::ScopedTestCrosSettings test_cros_settings_;
DeviceCloudPolicyStoreChromeOS* store_; DeviceCloudPolicyStoreChromeOS* store_;
DeviceCloudPolicyManagerChromeOS manager_; DeviceCloudPolicyManagerChromeOS manager_;
......
...@@ -279,6 +279,17 @@ message VariationsParameterProto { ...@@ -279,6 +279,17 @@ message VariationsParameterProto {
optional string parameter = 1; optional string parameter = 1;
} }
message AttestationSettingsProto {
// Attestation involves proving that a cryptographic key is protected by a
// legitimate Chrome OS TPM and reporting the operating mode of the platform.
// This setting enables attestation features at a device level. If this is
// enabled a machine key will be generated and certified by the Chrome OS
// CA. If this setting is disabled, the device will not communicate with the
// Chrome OS CA under any circumstances. Even users with attestation settings
// enabled will not be able to use those features on the device.
optional bool attestation_enabled = 1;
}
message ChromeDeviceSettingsProto { message ChromeDeviceSettingsProto {
optional DevicePolicyRefreshRateProto device_policy_refresh_rate = 1; optional DevicePolicyRefreshRateProto device_policy_refresh_rate = 1;
optional UserWhitelistProto user_whitelist = 2; optional UserWhitelistProto user_whitelist = 2;
...@@ -305,4 +316,5 @@ message ChromeDeviceSettingsProto { ...@@ -305,4 +316,5 @@ message ChromeDeviceSettingsProto {
optional StartUpFlagsProto start_up_flags = 23; optional StartUpFlagsProto start_up_flags = 23;
optional UptimeLimitProto uptime_limit = 24; optional UptimeLimitProto uptime_limit = 24;
optional VariationsParameterProto variations_parameter = 25; optional VariationsParameterProto variations_parameter = 25;
optional AttestationSettingsProto attestation_settings = 26;
} }
...@@ -49,6 +49,7 @@ const char* kKnownSettings[] = { ...@@ -49,6 +49,7 @@ const char* kKnownSettings[] = {
kAccountsPrefUsers, kAccountsPrefUsers,
kAllowRedeemChromeOsRegistrationOffers, kAllowRedeemChromeOsRegistrationOffers,
kAppPack, kAppPack,
kDeviceAttestationEnabled,
kDeviceOwner, kDeviceOwner,
kIdleLogoutTimeout, kIdleLogoutTimeout,
kIdleLogoutWarningDuration, kIdleLogoutWarningDuration,
...@@ -63,10 +64,10 @@ const char* kKnownSettings[] = { ...@@ -63,10 +64,10 @@ const char* kKnownSettings[] = {
kScreenSaverTimeout, kScreenSaverTimeout,
kSettingProxyEverywhere, kSettingProxyEverywhere,
kSignedDataRoamingEnabled, kSignedDataRoamingEnabled,
kStartUpFlags,
kStartUpUrls, kStartUpUrls,
kStatsReportingPref, kStatsReportingPref,
kSystemTimezonePolicy, kSystemTimezonePolicy,
kStartUpFlags,
kVariationsRestrictParameter, kVariationsRestrictParameter,
}; };
...@@ -341,6 +342,7 @@ void DeviceSettingsProvider::SetInPolicy() { ...@@ -341,6 +342,7 @@ void DeviceSettingsProvider::SetInPolicy() {
// The remaining settings don't support Set(), since they are not // The remaining settings don't support Set(), since they are not
// intended to be customizable by the user: // intended to be customizable by the user:
// kAppPack // kAppPack
// kDeviceAttestationEnabled
// kDeviceOwner // kDeviceOwner
// kIdleLogoutTimeout // kIdleLogoutTimeout
// kIdleLogoutWarningDuration // kIdleLogoutWarningDuration
...@@ -626,6 +628,10 @@ void DeviceSettingsProvider::DecodeGenericPolicies( ...@@ -626,6 +628,10 @@ void DeviceSettingsProvider::DecodeGenericPolicies(
kVariationsRestrictParameter, kVariationsRestrictParameter,
policy.variations_parameter().parameter()); policy.variations_parameter().parameter());
} }
new_values_cache->SetBoolean(
kDeviceAttestationEnabled,
policy.attestation_settings().attestation_enabled());
} }
void DeviceSettingsProvider::UpdateValuesCache( void DeviceSettingsProvider::UpdateValuesCache(
......
...@@ -125,6 +125,10 @@ ...@@ -125,6 +125,10 @@
'browser/chromeos/app_mode/kiosk_app_update_service.h', 'browser/chromeos/app_mode/kiosk_app_update_service.h',
'browser/chromeos/app_mode/startup_app_launcher.cc', 'browser/chromeos/app_mode/startup_app_launcher.cc',
'browser/chromeos/app_mode/startup_app_launcher.h', 'browser/chromeos/app_mode/startup_app_launcher.h',
'browser/chromeos/attestation/attestation_ca_client.cc',
'browser/chromeos/attestation/attestation_ca_client.h',
'browser/chromeos/attestation/attestation_policy_observer.cc',
'browser/chromeos/attestation/attestation_policy_observer.h',
'browser/chromeos/audio/audio_handler.cc', 'browser/chromeos/audio/audio_handler.cc',
'browser/chromeos/audio/audio_handler.h', 'browser/chromeos/audio/audio_handler.h',
'browser/chromeos/audio/audio_pref_handler_impl.cc', 'browser/chromeos/audio/audio_pref_handler_impl.cc',
......
...@@ -523,6 +523,7 @@ ...@@ -523,6 +523,7 @@
'browser/chrome_page_zoom_unittest.cc', 'browser/chrome_page_zoom_unittest.cc',
'browser/chrome_process_singleton_win_unittest.cc', 'browser/chrome_process_singleton_win_unittest.cc',
'browser/chromeos/accessibility/magnification_manager_unittest.cc', 'browser/chromeos/accessibility/magnification_manager_unittest.cc',
'browser/chromeos/attestation/attestation_policy_observer_unittest.cc',
'browser/chromeos/contacts/contact_database_unittest.cc', 'browser/chromeos/contacts/contact_database_unittest.cc',
'browser/chromeos/contacts/contact_manager_stub.cc', 'browser/chromeos/contacts/contact_manager_stub.cc',
'browser/chromeos/contacts/contact_manager_stub.h', 'browser/chromeos/contacts/contact_manager_stub.h',
......
mnissler@chromium.org
pastarmovj@chromium.org
...@@ -44,11 +44,11 @@ const char AttestationFlow::kEnterpriseMachineKey[] = "attest-ent-machine"; ...@@ -44,11 +44,11 @@ const char AttestationFlow::kEnterpriseMachineKey[] = "attest-ent-machine";
AttestationFlow::AttestationFlow(cryptohome::AsyncMethodCaller* async_caller, AttestationFlow::AttestationFlow(cryptohome::AsyncMethodCaller* async_caller,
CryptohomeClient* cryptohome_client, CryptohomeClient* cryptohome_client,
ServerProxy* server_proxy) scoped_ptr<ServerProxy> server_proxy)
: ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), : ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
async_caller_(async_caller), async_caller_(async_caller),
cryptohome_client_(cryptohome_client), cryptohome_client_(cryptohome_client),
server_proxy_(server_proxy) { server_proxy_(server_proxy.Pass()) {
} }
AttestationFlow::~AttestationFlow() { AttestationFlow::~AttestationFlow() {
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/callback_forward.h" #include "base/callback_forward.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "chromeos/chromeos_export.h" #include "chromeos/chromeos_export.h"
#include "chromeos/dbus/dbus_method_call_status.h" #include "chromeos/dbus/dbus_method_call_status.h"
...@@ -54,7 +55,7 @@ class CHROMEOS_EXPORT AttestationFlow { ...@@ -54,7 +55,7 @@ class CHROMEOS_EXPORT AttestationFlow {
AttestationFlow(cryptohome::AsyncMethodCaller* async_caller, AttestationFlow(cryptohome::AsyncMethodCaller* async_caller,
CryptohomeClient* cryptohome_client, CryptohomeClient* cryptohome_client,
ServerProxy* server_proxy); scoped_ptr<ServerProxy> server_proxy);
virtual ~AttestationFlow(); virtual ~AttestationFlow();
// Asynchronously gets an attestation certificate bound to the given name. // Asynchronously gets an attestation certificate bound to the given name.
...@@ -165,7 +166,7 @@ class CHROMEOS_EXPORT AttestationFlow { ...@@ -165,7 +166,7 @@ class CHROMEOS_EXPORT AttestationFlow {
base::WeakPtrFactory<AttestationFlow> weak_factory_; base::WeakPtrFactory<AttestationFlow> weak_factory_;
cryptohome::AsyncMethodCaller* async_caller_; cryptohome::AsyncMethodCaller* async_caller_;
CryptohomeClient* cryptohome_client_; CryptohomeClient* cryptohome_client_;
ServerProxy* server_proxy_; scoped_ptr<ServerProxy> server_proxy_;
DISALLOW_COPY_AND_ASSIGN(AttestationFlow); DISALLOW_COPY_AND_ASSIGN(AttestationFlow);
}; };
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "base/bind.h" #include "base/bind.h"
#include "base/memory/scoped_ptr.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "chromeos/attestation/mock_attestation_flow.h" #include "chromeos/attestation/mock_attestation_flow.h"
#include "chromeos/cryptohome/mock_async_method_caller.h" #include "chromeos/cryptohome/mock_async_method_caller.h"
...@@ -68,9 +69,9 @@ TEST_F(AttestationFlowTest, GetCertificate) { ...@@ -68,9 +69,9 @@ TEST_F(AttestationFlowTest, GetCertificate) {
.Times(1) .Times(1)
.InSequence(flow_order); .InSequence(flow_order);
StrictMock<MockServerProxy> proxy; scoped_ptr<MockServerProxy> proxy(new StrictMock<MockServerProxy>());
proxy.DeferToFake(true); proxy->DeferToFake(true);
EXPECT_CALL(proxy, SendEnrollRequest( EXPECT_CALL(*proxy, SendEnrollRequest(
cryptohome::MockAsyncMethodCaller::kFakeAttestationEnrollRequest, cryptohome::MockAsyncMethodCaller::kFakeAttestationEnrollRequest,
_)).Times(1) _)).Times(1)
.InSequence(flow_order); .InSequence(flow_order);
...@@ -88,7 +89,7 @@ TEST_F(AttestationFlowTest, GetCertificate) { ...@@ -88,7 +89,7 @@ TEST_F(AttestationFlowTest, GetCertificate) {
.Times(1) .Times(1)
.InSequence(flow_order); .InSequence(flow_order);
EXPECT_CALL(proxy, SendCertificateRequest( EXPECT_CALL(*proxy, SendCertificateRequest(
cryptohome::MockAsyncMethodCaller::kFakeAttestationCertRequest, cryptohome::MockAsyncMethodCaller::kFakeAttestationCertRequest,
_)).Times(1) _)).Times(1)
.InSequence(flow_order); .InSequence(flow_order);
...@@ -114,7 +115,8 @@ TEST_F(AttestationFlowTest, GetCertificate) { ...@@ -114,7 +115,8 @@ TEST_F(AttestationFlowTest, GetCertificate) {
&MockObserver::MockCertificateCallback, &MockObserver::MockCertificateCallback,
base::Unretained(&observer)); base::Unretained(&observer));
AttestationFlow flow(&async_caller, &client, &proxy); scoped_ptr<ServerProxy> proxy_interface(proxy.release());
AttestationFlow flow(&async_caller, &client, proxy_interface.Pass());
flow.GetCertificate("test", mock_callback); flow.GetCertificate("test", mock_callback);
Run(); Run();
} }
...@@ -130,7 +132,7 @@ TEST_F(AttestationFlowTest, GetCertificate_NoEK) { ...@@ -130,7 +132,7 @@ TEST_F(AttestationFlowTest, GetCertificate_NoEK) {
.WillRepeatedly(Invoke(DBusCallbackFalse)); .WillRepeatedly(Invoke(DBusCallbackFalse));
// We're not expecting any server calls in this case; StrictMock will verify. // We're not expecting any server calls in this case; StrictMock will verify.
StrictMock<MockServerProxy> proxy; scoped_ptr<MockServerProxy> proxy(new StrictMock<MockServerProxy>());
StrictMock<MockObserver> observer; StrictMock<MockObserver> observer;
EXPECT_CALL(observer, MockCertificateCallback(false, "")) EXPECT_CALL(observer, MockCertificateCallback(false, ""))
...@@ -139,7 +141,8 @@ TEST_F(AttestationFlowTest, GetCertificate_NoEK) { ...@@ -139,7 +141,8 @@ TEST_F(AttestationFlowTest, GetCertificate_NoEK) {
&MockObserver::MockCertificateCallback, &MockObserver::MockCertificateCallback,
base::Unretained(&observer)); base::Unretained(&observer));
AttestationFlow flow(&async_caller, &client, &proxy); scoped_ptr<ServerProxy> proxy_interface(proxy.release());
AttestationFlow flow(&async_caller, &client, proxy_interface.Pass());
flow.GetCertificate("test", mock_callback); flow.GetCertificate("test", mock_callback);
Run(); Run();
} }
...@@ -154,9 +157,9 @@ TEST_F(AttestationFlowTest, GetCertificate_EKRejected) { ...@@ -154,9 +157,9 @@ TEST_F(AttestationFlowTest, GetCertificate_EKRejected) {
EXPECT_CALL(client, TpmAttestationIsEnrolled(_)) EXPECT_CALL(client, TpmAttestationIsEnrolled(_))
.WillRepeatedly(Invoke(DBusCallbackFalse)); .WillRepeatedly(Invoke(DBusCallbackFalse));
StrictMock<MockServerProxy> proxy; scoped_ptr<MockServerProxy> proxy(new StrictMock<MockServerProxy>());
proxy.DeferToFake(false); proxy->DeferToFake(false);
EXPECT_CALL(proxy, SendEnrollRequest( EXPECT_CALL(*proxy, SendEnrollRequest(
cryptohome::MockAsyncMethodCaller::kFakeAttestationEnrollRequest, cryptohome::MockAsyncMethodCaller::kFakeAttestationEnrollRequest,
_)).Times(1); _)).Times(1);
...@@ -167,7 +170,8 @@ TEST_F(AttestationFlowTest, GetCertificate_EKRejected) { ...@@ -167,7 +170,8 @@ TEST_F(AttestationFlowTest, GetCertificate_EKRejected) {
&MockObserver::MockCertificateCallback, &MockObserver::MockCertificateCallback,
base::Unretained(&observer)); base::Unretained(&observer));
AttestationFlow flow(&async_caller, &client, &proxy); scoped_ptr<ServerProxy> proxy_interface(proxy.release());
AttestationFlow flow(&async_caller, &client, proxy_interface.Pass());
flow.GetCertificate("test", mock_callback); flow.GetCertificate("test", mock_callback);
Run(); Run();
} }
...@@ -187,9 +191,9 @@ TEST_F(AttestationFlowTest, GetCertificate_FailEnroll) { ...@@ -187,9 +191,9 @@ TEST_F(AttestationFlowTest, GetCertificate_FailEnroll) {
EXPECT_CALL(client, TpmAttestationIsEnrolled(_)) EXPECT_CALL(client, TpmAttestationIsEnrolled(_))
.WillRepeatedly(Invoke(DBusCallbackFalse)); .WillRepeatedly(Invoke(DBusCallbackFalse));
StrictMock<MockServerProxy> proxy; scoped_ptr<MockServerProxy> proxy(new StrictMock<MockServerProxy>());
proxy.DeferToFake(true); proxy->DeferToFake(true);
EXPECT_CALL(proxy, SendEnrollRequest( EXPECT_CALL(*proxy, SendEnrollRequest(
cryptohome::MockAsyncMethodCaller::kFakeAttestationEnrollRequest, cryptohome::MockAsyncMethodCaller::kFakeAttestationEnrollRequest,
_)).Times(1); _)).Times(1);
...@@ -199,7 +203,8 @@ TEST_F(AttestationFlowTest, GetCertificate_FailEnroll) { ...@@ -199,7 +203,8 @@ TEST_F(AttestationFlowTest, GetCertificate_FailEnroll) {
&MockObserver::MockCertificateCallback, &MockObserver::MockCertificateCallback,
base::Unretained(&observer)); base::Unretained(&observer));
AttestationFlow flow(&async_caller, &client, &proxy); scoped_ptr<ServerProxy> proxy_interface(proxy.release());
AttestationFlow flow(&async_caller, &client, proxy_interface.Pass());
flow.GetCertificate("test", mock_callback); flow.GetCertificate("test", mock_callback);
Run(); Run();
} }
...@@ -225,9 +230,9 @@ TEST_F(AttestationFlowTest, GetOwnerCertificateAlreadyEnrolled) { ...@@ -225,9 +230,9 @@ TEST_F(AttestationFlowTest, GetOwnerCertificateAlreadyEnrolled) {
EXPECT_CALL(client, TpmAttestationIsEnrolled(_)) EXPECT_CALL(client, TpmAttestationIsEnrolled(_))
.WillRepeatedly(Invoke(DBusCallbackTrue)); .WillRepeatedly(Invoke(DBusCallbackTrue));
StrictMock<MockServerProxy> proxy; scoped_ptr<MockServerProxy> proxy(new StrictMock<MockServerProxy>());
proxy.DeferToFake(true); proxy->DeferToFake(true);
EXPECT_CALL(proxy, SendCertificateRequest( EXPECT_CALL(*proxy, SendCertificateRequest(
cryptohome::MockAsyncMethodCaller::kFakeAttestationCertRequest, cryptohome::MockAsyncMethodCaller::kFakeAttestationCertRequest,
_)).Times(1); _)).Times(1);
...@@ -239,7 +244,8 @@ TEST_F(AttestationFlowTest, GetOwnerCertificateAlreadyEnrolled) { ...@@ -239,7 +244,8 @@ TEST_F(AttestationFlowTest, GetOwnerCertificateAlreadyEnrolled) {
&MockObserver::MockCertificateCallback, &MockObserver::MockCertificateCallback,
base::Unretained(&observer)); base::Unretained(&observer));
AttestationFlow flow(&async_caller, &client, &proxy); scoped_ptr<ServerProxy> proxy_interface(proxy.release());
AttestationFlow flow(&async_caller, &client, proxy_interface.Pass());
flow.GetCertificate("attest-ent-machine", mock_callback); flow.GetCertificate("attest-ent-machine", mock_callback);
Run(); Run();
} }
...@@ -257,7 +263,7 @@ TEST_F(AttestationFlowTest, GetCertificate_FailCreateCertRequest) { ...@@ -257,7 +263,7 @@ TEST_F(AttestationFlowTest, GetCertificate_FailCreateCertRequest) {
.WillRepeatedly(Invoke(DBusCallbackTrue)); .WillRepeatedly(Invoke(DBusCallbackTrue));
// We're not expecting any server calls in this case; StrictMock will verify. // We're not expecting any server calls in this case; StrictMock will verify.
StrictMock<MockServerProxy> proxy; scoped_ptr<MockServerProxy> proxy(new StrictMock<MockServerProxy>());
StrictMock<MockObserver> observer; StrictMock<MockObserver> observer;
EXPECT_CALL(observer, MockCertificateCallback(false, "")).Times(1); EXPECT_CALL(observer, MockCertificateCallback(false, "")).Times(1);
...@@ -265,7 +271,8 @@ TEST_F(AttestationFlowTest, GetCertificate_FailCreateCertRequest) { ...@@ -265,7 +271,8 @@ TEST_F(AttestationFlowTest, GetCertificate_FailCreateCertRequest) {
&MockObserver::MockCertificateCallback, &MockObserver::MockCertificateCallback,
base::Unretained(&observer)); base::Unretained(&observer));
AttestationFlow flow(&async_caller, &client, &proxy); scoped_ptr<ServerProxy> proxy_interface(proxy.release());
AttestationFlow flow(&async_caller, &client, proxy_interface.Pass());
flow.GetCertificate("test", mock_callback); flow.GetCertificate("test", mock_callback);
Run(); Run();
} }
...@@ -282,9 +289,9 @@ TEST_F(AttestationFlowTest, GetCertificate_CertRequestRejected) { ...@@ -282,9 +289,9 @@ TEST_F(AttestationFlowTest, GetCertificate_CertRequestRejected) {
EXPECT_CALL(client, TpmAttestationIsEnrolled(_)) EXPECT_CALL(client, TpmAttestationIsEnrolled(_))
.WillRepeatedly(Invoke(DBusCallbackTrue)); .WillRepeatedly(Invoke(DBusCallbackTrue));
StrictMock<MockServerProxy> proxy; scoped_ptr<MockServerProxy> proxy(new StrictMock<MockServerProxy>());
proxy.DeferToFake(false); proxy->DeferToFake(false);
EXPECT_CALL(proxy, SendCertificateRequest( EXPECT_CALL(*proxy, SendCertificateRequest(
cryptohome::MockAsyncMethodCaller::kFakeAttestationCertRequest, cryptohome::MockAsyncMethodCaller::kFakeAttestationCertRequest,
_)).Times(1); _)).Times(1);
...@@ -294,7 +301,8 @@ TEST_F(AttestationFlowTest, GetCertificate_CertRequestRejected) { ...@@ -294,7 +301,8 @@ TEST_F(AttestationFlowTest, GetCertificate_CertRequestRejected) {
&MockObserver::MockCertificateCallback, &MockObserver::MockCertificateCallback,
base::Unretained(&observer)); base::Unretained(&observer));
AttestationFlow flow(&async_caller, &client, &proxy); scoped_ptr<ServerProxy> proxy_interface(proxy.release());
AttestationFlow flow(&async_caller, &client, proxy_interface.Pass());
flow.GetCertificate("test", mock_callback); flow.GetCertificate("test", mock_callback);
Run(); Run();
} }
...@@ -308,7 +316,7 @@ TEST_F(AttestationFlowTest, GetCertificate_FailIsEnrolled) { ...@@ -308,7 +316,7 @@ TEST_F(AttestationFlowTest, GetCertificate_FailIsEnrolled) {
.WillRepeatedly(Invoke(DBusCallbackFail)); .WillRepeatedly(Invoke(DBusCallbackFail));
// We're not expecting any server calls in this case; StrictMock will verify. // We're not expecting any server calls in this case; StrictMock will verify.
StrictMock<MockServerProxy> proxy; scoped_ptr<MockServerProxy> proxy(new StrictMock<MockServerProxy>());
StrictMock<MockObserver> observer; StrictMock<MockObserver> observer;
EXPECT_CALL(observer, MockCertificateCallback(false, "")).Times(1); EXPECT_CALL(observer, MockCertificateCallback(false, "")).Times(1);
...@@ -316,7 +324,8 @@ TEST_F(AttestationFlowTest, GetCertificate_FailIsEnrolled) { ...@@ -316,7 +324,8 @@ TEST_F(AttestationFlowTest, GetCertificate_FailIsEnrolled) {
&MockObserver::MockCertificateCallback, &MockObserver::MockCertificateCallback,
base::Unretained(&observer)); base::Unretained(&observer));
AttestationFlow flow(&async_caller, &client, &proxy); scoped_ptr<ServerProxy> proxy_interface(proxy.release());
AttestationFlow flow(&async_caller, &client, proxy_interface.Pass());
flow.GetCertificate("test", mock_callback); flow.GetCertificate("test", mock_callback);
Run(); Run();
} }
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "chromeos/attestation/mock_attestation_flow.h" #include "chromeos/attestation/mock_attestation_flow.h"
#include "base/memory/scoped_ptr.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
using testing::_; using testing::_;
...@@ -42,5 +43,10 @@ MockObserver::MockObserver() {} ...@@ -42,5 +43,10 @@ MockObserver::MockObserver() {}
MockObserver::~MockObserver() {} MockObserver::~MockObserver() {}
MockAttestationFlow::MockAttestationFlow()
: AttestationFlow(NULL, NULL, scoped_ptr<ServerProxy>()) {}
MockAttestationFlow::~MockAttestationFlow() {}
} // namespace attestation } // namespace attestation
} // namespace chromeos } // namespace chromeos
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef CHROMEOS_ATTESTATION_MOCK_ATTESTATION_FLOW_H_
#define CHROMEOS_ATTESTATION_MOCK_ATTESTATION_FLOW_H_
#include "chromeos/attestation/attestation_flow.h" #include "chromeos/attestation/attestation_flow.h"
#include "base/basictypes.h" #include "base/basictypes.h"
...@@ -57,5 +60,16 @@ class MockObserver { ...@@ -57,5 +60,16 @@ class MockObserver {
MOCK_METHOD2(MockCertificateCallback, void(bool, const std::string&)); MOCK_METHOD2(MockCertificateCallback, void(bool, const std::string&));
}; };
class MockAttestationFlow : public AttestationFlow {
public:
MockAttestationFlow();
virtual ~MockAttestationFlow();
MOCK_METHOD2(GetCertificate, void(const std::string&,
const CertificateCallback&));
};
} // namespace attestation } // namespace attestation
} // namespace chromeos } // namespace chromeos
#endif // CHROMEOS_ATTESTATION_MOCK_ATTESTATION_FLOW_H_
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