1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
|
// Copyright 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_PLATFORM_VERIFICATION_FLOW_H_
#define CHROME_BROWSER_CHROMEOS_ATTESTATION_PLATFORM_VERIFICATION_FLOW_H_
#include <string>
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "url/gurl.h"
class HostContentSettingsMap;
class PrefService;
namespace content {
class WebContents;
}
namespace cryptohome {
class AsyncMethodCaller;
}
namespace user_manager {
class User;
}
namespace user_prefs {
class PrefRegistrySyncable;
}
namespace chromeos {
class CryptohomeClient;
namespace attestation {
class AttestationFlow;
class PlatformVerificationFlowTest;
// This class allows platform verification for the content protection use case.
// All methods must only be called on the UI thread. Example:
// scoped_refptr<PlatformVerificationFlow> verifier =
// new PlatformVerificationFlow();
// PlatformVerificationFlow::Callback callback = base::Bind(&MyCallback);
// verifier->ChallengePlatformKey(my_web_contents, "my_id", "some_challenge",
// callback);
//
// This class is RefCountedThreadSafe because it may need to outlive its caller.
// The attestation flow that needs to happen to establish a certified platform
// key may take minutes on some hardware. This class will timeout after a much
// shorter time so the caller can proceed without platform verification but it
// is important that the pending operation be allowed to finish. If the
// attestation flow is aborted at any stage, it will need to start over. If we
// use weak pointers, the attestation flow will stop when the next callback is
// run. So we need the instance to stay alive until the platform key is fully
// certified so the next time ChallegePlatformKey() is invoked it will be quick.
class PlatformVerificationFlow
: public base::RefCountedThreadSafe<PlatformVerificationFlow> {
public:
enum Result {
SUCCESS, // The operation succeeded.
INTERNAL_ERROR, // The operation failed unexpectedly.
PLATFORM_NOT_VERIFIED, // The platform cannot be verified. For example:
// - It is not a Chrome device.
// - It is not running a verified OS image.
USER_REJECTED, // The user explicitly rejected the operation.
POLICY_REJECTED, // The operation is not allowed by policy/settings.
TIMEOUT, // The operation timed out.
};
enum ConsentResponse {
CONSENT_RESPONSE_NONE,
CONSENT_RESPONSE_ALLOW,
CONSENT_RESPONSE_DENY,
};
// An interface which allows settings and UI to be abstracted for testing
// purposes. For normal operation the default implementation should be used.
class Delegate {
public:
virtual ~Delegate() {}
// This callback will be called when a user has given a |response| to a
// consent request of the specified |type|.
typedef base::Callback<void(ConsentResponse response)> ConsentCallback;
// Invokes consent UI within the context of |web_contents| and calls
// |callback| when the user responds.
// Precondition: The last committed URL for |web_contents| has a valid
// origin.
virtual void ShowConsentPrompt(content::WebContents* web_contents,
const ConsentCallback& callback) = 0;
// Gets prefs associated with the given |web_contents|. If no prefs are
// associated with |web_contents| then NULL is returned.
virtual PrefService* GetPrefs(content::WebContents* web_contents) = 0;
// Gets the URL associated with the given |web_contents|.
virtual const GURL& GetURL(content::WebContents* web_contents) = 0;
// Gets the user associated with the given |web_contents|. NULL may be
// returned.
virtual user_manager::User* GetUser(content::WebContents* web_contents) = 0;
// Gets the content settings map associated with the given |web_contents|.
virtual HostContentSettingsMap* GetContentSettings(
content::WebContents* web_contents) = 0;
// Returns true iff |web_contents| belongs to a guest or incognito session.
virtual bool IsGuestOrIncognito(content::WebContents* web_contents) = 0;
};
// This callback will be called when a challenge operation completes. If
// |result| is SUCCESS then |signed_data| holds the data which was signed
// by the platform key (this is the original challenge appended with a random
// nonce) and |signature| holds the RSA-PKCS1-v1.5 signature. The
// |platform_key_certificate| certifies the key used to generate the
// signature. This key may be generated on demand and is not guaranteed to
// persist across multiple calls to this method. The browser does not check
// the validity of |signature| or |platform_key_certificate|.
typedef base::Callback<void(Result result,
const std::string& signed_data,
const std::string& signature,
const std::string& platform_key_certificate)>
ChallengeCallback;
// A constructor that uses the default implementation of all dependencies
// including Delegate.
PlatformVerificationFlow();
// An alternate constructor which specifies dependent objects explicitly.
// This is useful in testing. The caller retains ownership of all pointers.
PlatformVerificationFlow(AttestationFlow* attestation_flow,
cryptohome::AsyncMethodCaller* async_caller,
CryptohomeClient* cryptohome_client,
Delegate* delegate);
// Invokes an asynchronous operation to challenge a platform key. Any user
// interaction will be associated with |web_contents|. The |service_id| is an
// arbitrary value but it should uniquely identify the origin of the request
// and should not be determined by that origin; its purpose is to prevent
// collusion between multiple services. The |challenge| is also an arbitrary
// value but it should be time sensitive or associated to some kind of session
// because its purpose is to prevent certificate replay. The |callback| will
// be called when the operation completes. The duration of the operation can
// vary depending on system state, hardware capabilities, and interaction with
// the user.
void ChallengePlatformKey(content::WebContents* web_contents,
const std::string& service_id,
const std::string& challenge,
const ChallengeCallback& callback);
static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* prefs);
void set_timeout_delay(const base::TimeDelta& timeout_delay) {
timeout_delay_ = timeout_delay;
}
private:
friend class base::RefCountedThreadSafe<PlatformVerificationFlow>;
friend class PlatformVerificationFlowTest;
// Holds the arguments of a ChallengePlatformKey call. This is convenient for
// use with base::Bind so we don't get too many arguments.
struct ChallengeContext {
ChallengeContext(content::WebContents* web_contents,
const std::string& service_id,
const std::string& challenge,
const ChallengeCallback& callback);
~ChallengeContext();
content::WebContents* web_contents;
std::string service_id;
std::string challenge;
ChallengeCallback callback;
};
~PlatformVerificationFlow();
// Checks whether we need to prompt the user for consent before proceeding and
// invokes the consent UI if so. The arguments to ChallengePlatformKey are
// in |context| and |attestation_enrolled| specifies whether attestation has
// been enrolled for this device.
void CheckConsent(const ChallengeContext& context,
bool attestation_enrolled);
// A callback called when the user has given their consent response. The
// arguments to ChallengePlatformKey are in |context|. |consent_required| and
// |consent_response| indicate whether consent was required and user response,
// respectively. If the response indicates that the operation should proceed,
// this method invokes a certificate request.
void OnConsentResponse(const ChallengeContext& context,
bool consent_required,
ConsentResponse consent_response);
// Initiates the flow to get a platform key certificate. The arguments to
// ChallengePlatformKey are in |context|. |user_id| identifies the user for
// which to get a certificate. If |force_new_key| is true then any existing
// key for the same user and service will be ignored and a new key will be
// generated and certified.
void GetCertificate(const ChallengeContext& context,
const std::string& user_id,
bool force_new_key);
// A callback called when an attestation certificate request operation
// completes. The arguments to ChallengePlatformKey are in |context|.
// |user_id| identifies the user for which the certificate was requested.
// |operation_success| is true iff the certificate request operation
// succeeded. |certificate| holds the certificate for the platform key on
// success. If the certificate request was successful, this method invokes a
// request to sign the challenge. If the operation timed out prior to this
// method being called, this method does nothing - notably, the callback is
// not invoked.
void OnCertificateReady(const ChallengeContext& context,
const std::string& user_id,
scoped_ptr<base::Timer> timer,
bool operation_success,
const std::string& certificate);
// A callback run after a constant delay to handle timeouts for lengthy
// certificate requests. |context.callback| will be invoked with a TIMEOUT
// result.
void OnCertificateTimeout(const ChallengeContext& context);
// A callback called when a challenge signing request has completed. The
// |certificate| is the platform certificate for the key which signed the
// |challenge|. The arguments to ChallengePlatformKey are in |context|.
// |operation_success| is true iff the challenge signing operation was
// successful. If it was successful, |response_data| holds the challenge
// response and the method will invoke |context.callback|.
void OnChallengeReady(const ChallengeContext& context,
const std::string& certificate,
bool operation_success,
const std::string& response_data);
// Checks whether policy or profile settings associated with |web_contents|
// have attestation for content protection explicitly disabled.
bool IsAttestationEnabled(content::WebContents* web_contents);
// Updates user settings for the profile associated with |web_contents| based
// on the |consent_response| to the request of type |consent_type|.
bool UpdateSettings(content::WebContents* web_contents,
ConsentResponse consent_response);
// Finds the domain-specific consent pref in |content_settings| for |url|. If
// a pref exists for the domain, returns true and sets |pref_value| if it is
// not NULL.
bool GetDomainPref(HostContentSettingsMap* content_settings,
const GURL& url,
bool* pref_value);
// Records the domain-specific consent pref in |content_settings| for |url|.
// The pref will be set to |allow_domain|.
void RecordDomainConsent(HostContentSettingsMap* content_settings,
const GURL& url,
bool allow_domain);
// Returns true iff |certificate| is an expired X.509 certificate.
bool IsExpired(const std::string& certificate);
AttestationFlow* attestation_flow_;
scoped_ptr<AttestationFlow> default_attestation_flow_;
cryptohome::AsyncMethodCaller* async_caller_;
CryptohomeClient* cryptohome_client_;
Delegate* delegate_;
scoped_ptr<Delegate> default_delegate_;
base::TimeDelta timeout_delay_;
DISALLOW_COPY_AND_ASSIGN(PlatformVerificationFlow);
};
} // namespace attestation
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_ATTESTATION_PLATFORM_VERIFICATION_FLOW_H_
|