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