Home | History | Annotate | Download | only in attestation
      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:
     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     // Gets prefs associated with the given |web_contents|.  If no prefs are
     99     // associated with |web_contents| then NULL is returned.
    100     virtual PrefService* GetPrefs(content::WebContents* web_contents) = 0;
    101 
    102     // Gets the URL associated with the given |web_contents|.
    103     virtual const GURL& GetURL(content::WebContents* web_contents) = 0;
    104 
    105     // Gets the user associated with the given |web_contents|.  NULL may be
    106     // returned.
    107     virtual User* GetUser(content::WebContents* web_contents) = 0;
    108 
    109     // Gets the content settings map associated with the given |web_contents|.
    110     virtual HostContentSettingsMap* GetContentSettings(
    111         content::WebContents* web_contents) = 0;
    112 
    113     // Returns true iff |web_contents| belongs to a guest or incognito session.
    114     virtual bool IsGuestOrIncognito(content::WebContents* web_contents) = 0;
    115   };
    116 
    117   // This callback will be called when a challenge operation completes.  If
    118   // |result| is SUCCESS then |signed_data| holds the data which was signed
    119   // by the platform key (this is the original challenge appended with a random
    120   // nonce) and |signature| holds the RSA-PKCS1-v1.5 signature.  The
    121   // |platform_key_certificate| certifies the key used to generate the
    122   // signature.  This key may be generated on demand and is not guaranteed to
    123   // persist across multiple calls to this method.  The browser does not check
    124   // the validity of |signature| or |platform_key_certificate|.
    125   typedef base::Callback<void(Result result,
    126                               const std::string& signed_data,
    127                               const std::string& signature,
    128                               const std::string& platform_key_certificate)>
    129       ChallengeCallback;
    130 
    131   // A constructor that uses the default implementation of all dependencies
    132   // including Delegate.
    133   PlatformVerificationFlow();
    134 
    135   // An alternate constructor which specifies dependent objects explicitly.
    136   // This is useful in testing.  The caller retains ownership of all pointers.
    137   PlatformVerificationFlow(AttestationFlow* attestation_flow,
    138                            cryptohome::AsyncMethodCaller* async_caller,
    139                            CryptohomeClient* cryptohome_client,
    140                            Delegate* delegate);
    141 
    142   // Invokes an asynchronous operation to challenge a platform key.  Any user
    143   // interaction will be associated with |web_contents|.  The |service_id| is an
    144   // arbitrary value but it should uniquely identify the origin of the request
    145   // and should not be determined by that origin; its purpose is to prevent
    146   // collusion between multiple services.  The |challenge| is also an arbitrary
    147   // value but it should be time sensitive or associated to some kind of session
    148   // because its purpose is to prevent certificate replay.  The |callback| will
    149   // be called when the operation completes.  The duration of the operation can
    150   // vary depending on system state, hardware capabilities, and interaction with
    151   // the user.
    152   void ChallengePlatformKey(content::WebContents* web_contents,
    153                             const std::string& service_id,
    154                             const std::string& challenge,
    155                             const ChallengeCallback& callback);
    156 
    157   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* prefs);
    158 
    159   void set_timeout_delay(const base::TimeDelta& timeout_delay) {
    160     timeout_delay_ = timeout_delay;
    161   }
    162 
    163  private:
    164   friend class base::RefCountedThreadSafe<PlatformVerificationFlow>;
    165   friend class PlatformVerificationFlowTest;
    166 
    167   // Holds the arguments of a ChallengePlatformKey call.  This is convenient for
    168   // use with base::Bind so we don't get too many arguments.
    169   struct ChallengeContext {
    170     ChallengeContext(content::WebContents* web_contents,
    171                      const std::string& service_id,
    172                      const std::string& challenge,
    173                      const ChallengeCallback& callback);
    174     ~ChallengeContext();
    175 
    176     content::WebContents* web_contents;
    177     std::string service_id;
    178     std::string challenge;
    179     ChallengeCallback callback;
    180   };
    181 
    182   ~PlatformVerificationFlow();
    183 
    184   // Checks whether we need to prompt the user for consent before proceeding and
    185   // invokes the consent UI if so.  The arguments to ChallengePlatformKey are
    186   // in |context| and |attestation_enrolled| specifies whether attestation has
    187   // been enrolled for this device.
    188   void CheckConsent(const ChallengeContext& context,
    189                     bool attestation_enrolled);
    190 
    191   // A callback called when the user has given their consent response.  The
    192   // arguments to ChallengePlatformKey are in |context|.  |consent_required| and
    193   // |consent_response| indicate whether consent was required and user response,
    194   // respectively.  If the response indicates that the operation should proceed,
    195   // this method invokes a certificate request.
    196   void OnConsentResponse(const ChallengeContext& context,
    197                          bool consent_required,
    198                          ConsentResponse consent_response);
    199 
    200   // Initiates the flow to get a platform key certificate.  The arguments to
    201   // ChallengePlatformKey are in |context|.  |user_id| identifies the user for
    202   // which to get a certificate.  If |force_new_key| is true then any existing
    203   // key for the same user and service will be ignored and a new key will be
    204   // generated and certified.
    205   void GetCertificate(const ChallengeContext& context,
    206                       const std::string& user_id,
    207                       bool force_new_key);
    208 
    209   // A callback called when an attestation certificate request operation
    210   // completes.  The arguments to ChallengePlatformKey are in |context|.
    211   // |user_id| identifies the user for which the certificate was requested.
    212   // |operation_success| is true iff the certificate request operation
    213   // succeeded.  |certificate| holds the certificate for the platform key on
    214   // success.  If the certificate request was successful, this method invokes a
    215   // request to sign the challenge.  If the operation timed out prior to this
    216   // method being called, this method does nothing - notably, the callback is
    217   // not invoked.
    218   void OnCertificateReady(const ChallengeContext& context,
    219                           const std::string& user_id,
    220                           scoped_ptr<base::Timer> timer,
    221                           bool operation_success,
    222                           const std::string& certificate);
    223 
    224   // A callback run after a constant delay to handle timeouts for lengthy
    225   // certificate requests.  |context.callback| will be invoked with a TIMEOUT
    226   // result.
    227   void OnCertificateTimeout(const ChallengeContext& context);
    228 
    229   // A callback called when a challenge signing request has completed.  The
    230   // |certificate| is the platform certificate for the key which signed the
    231   // |challenge|.  The arguments to ChallengePlatformKey are in |context|.
    232   // |operation_success| is true iff the challenge signing operation was
    233   // successful.  If it was successful, |response_data| holds the challenge
    234   // response and the method will invoke |context.callback|.
    235   void OnChallengeReady(const ChallengeContext& context,
    236                         const std::string& certificate,
    237                         bool operation_success,
    238                         const std::string& response_data);
    239 
    240   // Checks whether policy or profile settings associated with |web_contents|
    241   // have attestation for content protection explicitly disabled.
    242   bool IsAttestationEnabled(content::WebContents* web_contents);
    243 
    244   // Updates user settings for the profile associated with |web_contents| based
    245   // on the |consent_response| to the request of type |consent_type|.
    246   bool UpdateSettings(content::WebContents* web_contents,
    247                       ConsentResponse consent_response);
    248 
    249   // Finds the domain-specific consent pref in |content_settings| for |url|.  If
    250   // a pref exists for the domain, returns true and sets |pref_value| if it is
    251   // not NULL.
    252   bool GetDomainPref(HostContentSettingsMap* content_settings,
    253                      const GURL& url,
    254                      bool* pref_value);
    255 
    256   // Records the domain-specific consent pref in |content_settings| for |url|.
    257   // The pref will be set to |allow_domain|.
    258   void RecordDomainConsent(HostContentSettingsMap* content_settings,
    259                            const GURL& url,
    260                            bool allow_domain);
    261 
    262   // Returns true iff |certificate| is an expired X.509 certificate.
    263   bool IsExpired(const std::string& certificate);
    264 
    265   AttestationFlow* attestation_flow_;
    266   scoped_ptr<AttestationFlow> default_attestation_flow_;
    267   cryptohome::AsyncMethodCaller* async_caller_;
    268   CryptohomeClient* cryptohome_client_;
    269   Delegate* delegate_;
    270   scoped_ptr<Delegate> default_delegate_;
    271   base::TimeDelta timeout_delay_;
    272 
    273   DISALLOW_COPY_AND_ASSIGN(PlatformVerificationFlow);
    274 };
    275 
    276 }  // namespace attestation
    277 }  // namespace chromeos
    278 
    279 #endif  // CHROME_BROWSER_CHROMEOS_ATTESTATION_PLATFORM_VERIFICATION_FLOW_H_
    280