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