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