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