1 // Copyright (c) 2012 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 #include "chromeos/attestation/attestation_flow.h" 6 7 #include "base/bind.h" 8 #include "chromeos/cryptohome/async_method_caller.h" 9 #include "chromeos/dbus/cryptohome_client.h" 10 11 namespace chromeos { 12 namespace attestation { 13 14 namespace { 15 16 // Redirects to one of three callbacks based on a boolean value and dbus call 17 // status. 18 // 19 // Parameters 20 // on_true - Called when status=succes and value=true. 21 // on_false - Called when status=success and value=false. 22 // on_fail - Called when status=failure. 23 // status - The D-Bus operation status. 24 // value - The value returned by the D-Bus operation. 25 void DBusBoolRedirectCallback(const base::Closure& on_true, 26 const base::Closure& on_false, 27 const base::Closure& on_fail, 28 DBusMethodCallStatus status, 29 bool value) { 30 if (status != DBUS_METHOD_CALL_SUCCESS) { 31 LOG(ERROR) << "Attestation: Failed to query enrollment state."; 32 if (!on_fail.is_null()) 33 on_fail.Run(); 34 return; 35 } 36 const base::Closure& task = value ? on_true : on_false; 37 if (!task.is_null()) 38 task.Run(); 39 } 40 41 void DBusDataMethodCallback( 42 const AttestationFlow::CertificateCallback& callback, 43 DBusMethodCallStatus status, 44 bool result, 45 const std::string& data) { 46 if (status != DBUS_METHOD_CALL_SUCCESS) { 47 LOG(ERROR) << "Attestation: DBus data operation failed."; 48 if (!callback.is_null()) 49 callback.Run(false, ""); 50 return; 51 } 52 if (!callback.is_null()) 53 callback.Run(result, data); 54 } 55 56 AttestationKeyType GetKeyTypeForProfile( 57 AttestationCertificateProfile profile) { 58 switch (profile) { 59 case PROFILE_ENTERPRISE_MACHINE_CERTIFICATE: 60 return KEY_DEVICE; 61 case PROFILE_ENTERPRISE_USER_CERTIFICATE: 62 case PROFILE_CONTENT_PROTECTION_CERTIFICATE: 63 return KEY_USER; 64 } 65 NOTREACHED(); 66 return KEY_USER; 67 } 68 69 std::string GetKeyNameForProfile(AttestationCertificateProfile profile, 70 const std::string& origin) { 71 switch (profile) { 72 case PROFILE_ENTERPRISE_MACHINE_CERTIFICATE: 73 return kEnterpriseMachineKey; 74 case PROFILE_ENTERPRISE_USER_CERTIFICATE: 75 return kEnterpriseUserKey; 76 case PROFILE_CONTENT_PROTECTION_CERTIFICATE: 77 return std::string(kContentProtectionKeyPrefix) + origin; 78 } 79 NOTREACHED(); 80 return ""; 81 } 82 83 } // namespace 84 85 AttestationFlow::AttestationFlow(cryptohome::AsyncMethodCaller* async_caller, 86 CryptohomeClient* cryptohome_client, 87 scoped_ptr<ServerProxy> server_proxy) 88 : async_caller_(async_caller), 89 cryptohome_client_(cryptohome_client), 90 server_proxy_(server_proxy.Pass()), 91 weak_factory_(this) { 92 } 93 94 AttestationFlow::~AttestationFlow() { 95 } 96 97 void AttestationFlow::GetCertificate( 98 AttestationCertificateProfile certificate_profile, 99 const std::string& user_id, 100 const std::string& request_origin, 101 bool force_new_key, 102 const CertificateCallback& callback) { 103 // If this device has not enrolled with the Privacy CA, we need to do that 104 // first. Once enrolled we can proceed with the certificate request. 105 base::Closure do_cert_request = base::Bind( 106 &AttestationFlow::StartCertificateRequest, 107 weak_factory_.GetWeakPtr(), 108 certificate_profile, 109 user_id, 110 request_origin, 111 force_new_key, 112 callback); 113 base::Closure on_enroll_failure = base::Bind(callback, false, ""); 114 base::Closure do_enroll = base::Bind(&AttestationFlow::StartEnroll, 115 weak_factory_.GetWeakPtr(), 116 on_enroll_failure, 117 do_cert_request); 118 cryptohome_client_->TpmAttestationIsEnrolled(base::Bind( 119 &DBusBoolRedirectCallback, 120 do_cert_request, // If enrolled, proceed with cert request. 121 do_enroll, // If not enrolled, initiate enrollment. 122 on_enroll_failure)); 123 } 124 125 void AttestationFlow::StartEnroll(const base::Closure& on_failure, 126 const base::Closure& next_task) { 127 // Get the attestation service to create a Privacy CA enrollment request. 128 async_caller_->AsyncTpmAttestationCreateEnrollRequest( 129 server_proxy_->GetType(), 130 base::Bind(&AttestationFlow::SendEnrollRequestToPCA, 131 weak_factory_.GetWeakPtr(), 132 on_failure, 133 next_task)); 134 } 135 136 void AttestationFlow::SendEnrollRequestToPCA(const base::Closure& on_failure, 137 const base::Closure& next_task, 138 bool success, 139 const std::string& data) { 140 if (!success) { 141 LOG(ERROR) << "Attestation: Failed to create enroll request."; 142 if (!on_failure.is_null()) 143 on_failure.Run(); 144 return; 145 } 146 147 // Send the request to the Privacy CA. 148 server_proxy_->SendEnrollRequest( 149 data, 150 base::Bind(&AttestationFlow::SendEnrollResponseToDaemon, 151 weak_factory_.GetWeakPtr(), 152 on_failure, 153 next_task)); 154 } 155 156 void AttestationFlow::SendEnrollResponseToDaemon( 157 const base::Closure& on_failure, 158 const base::Closure& next_task, 159 bool success, 160 const std::string& data) { 161 if (!success) { 162 LOG(ERROR) << "Attestation: Enroll request failed."; 163 if (!on_failure.is_null()) 164 on_failure.Run(); 165 return; 166 } 167 168 // Forward the response to the attestation service to complete enrollment. 169 async_caller_->AsyncTpmAttestationEnroll( 170 server_proxy_->GetType(), 171 data, 172 base::Bind(&AttestationFlow::OnEnrollComplete, 173 weak_factory_.GetWeakPtr(), 174 on_failure, 175 next_task)); 176 } 177 178 void AttestationFlow::OnEnrollComplete(const base::Closure& on_failure, 179 const base::Closure& next_task, 180 bool success, 181 cryptohome::MountError /*not_used*/) { 182 if (!success) { 183 LOG(ERROR) << "Attestation: Failed to complete enrollment."; 184 if (!on_failure.is_null()) 185 on_failure.Run(); 186 return; 187 } 188 189 // Enrollment has successfully completed, we can move on to whatever is next. 190 if (!next_task.is_null()) 191 next_task.Run(); 192 } 193 194 void AttestationFlow::StartCertificateRequest( 195 AttestationCertificateProfile certificate_profile, 196 const std::string& user_id, 197 const std::string& request_origin, 198 bool generate_new_key, 199 const CertificateCallback& callback) { 200 AttestationKeyType key_type = GetKeyTypeForProfile(certificate_profile); 201 std::string key_name = GetKeyNameForProfile(certificate_profile, 202 request_origin); 203 if (generate_new_key) { 204 // Get the attestation service to create a Privacy CA certificate request. 205 async_caller_->AsyncTpmAttestationCreateCertRequest( 206 server_proxy_->GetType(), 207 certificate_profile, 208 user_id, 209 request_origin, 210 base::Bind(&AttestationFlow::SendCertificateRequestToPCA, 211 weak_factory_.GetWeakPtr(), 212 key_type, 213 user_id, 214 key_name, 215 callback)); 216 } else { 217 // If the key already exists, query the existing certificate. 218 base::Closure on_key_exists = base::Bind( 219 &AttestationFlow::GetExistingCertificate, 220 weak_factory_.GetWeakPtr(), 221 key_type, 222 user_id, 223 key_name, 224 callback); 225 // If the key does not exist, call this method back with |generate_new_key| 226 // set to true. 227 base::Closure on_key_not_exists = base::Bind( 228 &AttestationFlow::StartCertificateRequest, 229 weak_factory_.GetWeakPtr(), 230 certificate_profile, 231 user_id, 232 request_origin, 233 true, 234 callback); 235 cryptohome_client_->TpmAttestationDoesKeyExist( 236 key_type, 237 user_id, 238 key_name, 239 base::Bind(&DBusBoolRedirectCallback, 240 on_key_exists, 241 on_key_not_exists, 242 base::Bind(callback, false, ""))); 243 } 244 } 245 246 void AttestationFlow::SendCertificateRequestToPCA( 247 AttestationKeyType key_type, 248 const std::string& user_id, 249 const std::string& key_name, 250 const CertificateCallback& callback, 251 bool success, 252 const std::string& data) { 253 if (!success) { 254 LOG(ERROR) << "Attestation: Failed to create certificate request."; 255 if (!callback.is_null()) 256 callback.Run(false, ""); 257 return; 258 } 259 260 // Send the request to the Privacy CA. 261 server_proxy_->SendCertificateRequest( 262 data, 263 base::Bind(&AttestationFlow::SendCertificateResponseToDaemon, 264 weak_factory_.GetWeakPtr(), 265 key_type, 266 user_id, 267 key_name, 268 callback)); 269 } 270 271 void AttestationFlow::SendCertificateResponseToDaemon( 272 AttestationKeyType key_type, 273 const std::string& user_id, 274 const std::string& key_name, 275 const CertificateCallback& callback, 276 bool success, 277 const std::string& data) { 278 if (!success) { 279 LOG(ERROR) << "Attestation: Certificate request failed."; 280 if (!callback.is_null()) 281 callback.Run(false, ""); 282 return; 283 } 284 285 // Forward the response to the attestation service to complete the operation. 286 async_caller_->AsyncTpmAttestationFinishCertRequest(data, 287 key_type, 288 user_id, 289 key_name, 290 base::Bind(callback)); 291 } 292 293 void AttestationFlow::GetExistingCertificate( 294 AttestationKeyType key_type, 295 const std::string& user_id, 296 const std::string& key_name, 297 const CertificateCallback& callback) { 298 cryptohome_client_->TpmAttestationGetCertificate( 299 key_type, 300 user_id, 301 key_name, 302 base::Bind(&DBusDataMethodCallback, callback)); 303 } 304 305 ServerProxy::~ServerProxy() {} 306 307 PrivacyCAType ServerProxy::GetType() { 308 return DEFAULT_PCA; 309 } 310 311 } // namespace attestation 312 } // namespace chromeos 313