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.
DBusBoolRedirectCallback(const base::Closure & on_true,const base::Closure & on_false,const base::Closure & on_fail,DBusMethodCallStatus status,bool value)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
DBusDataMethodCallback(const AttestationFlow::CertificateCallback & callback,DBusMethodCallStatus status,bool result,const std::string & data)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
GetKeyTypeForProfile(AttestationCertificateProfile profile)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
GetKeyNameForProfile(AttestationCertificateProfile profile,const std::string & origin)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
AttestationFlow(cryptohome::AsyncMethodCaller * async_caller,CryptohomeClient * cryptohome_client,scoped_ptr<ServerProxy> server_proxy)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
~AttestationFlow()94 AttestationFlow::~AttestationFlow() {
95 }
96
GetCertificate(AttestationCertificateProfile certificate_profile,const std::string & user_id,const std::string & request_origin,bool force_new_key,const CertificateCallback & callback)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
StartEnroll(const base::Closure & on_failure,const base::Closure & next_task)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(base::Bind(
129 &AttestationFlow::SendEnrollRequestToPCA,
130 weak_factory_.GetWeakPtr(),
131 on_failure,
132 next_task));
133 }
134
SendEnrollRequestToPCA(const base::Closure & on_failure,const base::Closure & next_task,bool success,const std::string & data)135 void AttestationFlow::SendEnrollRequestToPCA(const base::Closure& on_failure,
136 const base::Closure& next_task,
137 bool success,
138 const std::string& data) {
139 if (!success) {
140 LOG(ERROR) << "Attestation: Failed to create enroll request.";
141 if (!on_failure.is_null())
142 on_failure.Run();
143 return;
144 }
145
146 // Send the request to the Privacy CA.
147 server_proxy_->SendEnrollRequest(
148 data,
149 base::Bind(&AttestationFlow::SendEnrollResponseToDaemon,
150 weak_factory_.GetWeakPtr(),
151 on_failure,
152 next_task));
153 }
154
SendEnrollResponseToDaemon(const base::Closure & on_failure,const base::Closure & next_task,bool success,const std::string & data)155 void AttestationFlow::SendEnrollResponseToDaemon(
156 const base::Closure& on_failure,
157 const base::Closure& next_task,
158 bool success,
159 const std::string& data) {
160 if (!success) {
161 LOG(ERROR) << "Attestation: Enroll request failed.";
162 if (!on_failure.is_null())
163 on_failure.Run();
164 return;
165 }
166
167 // Forward the response to the attestation service to complete enrollment.
168 async_caller_->AsyncTpmAttestationEnroll(
169 data,
170 base::Bind(&AttestationFlow::OnEnrollComplete,
171 weak_factory_.GetWeakPtr(),
172 on_failure,
173 next_task));
174 }
175
OnEnrollComplete(const base::Closure & on_failure,const base::Closure & next_task,bool success,cryptohome::MountError)176 void AttestationFlow::OnEnrollComplete(const base::Closure& on_failure,
177 const base::Closure& next_task,
178 bool success,
179 cryptohome::MountError /*not_used*/) {
180 if (!success) {
181 LOG(ERROR) << "Attestation: Failed to complete enrollment.";
182 if (!on_failure.is_null())
183 on_failure.Run();
184 return;
185 }
186
187 // Enrollment has successfully completed, we can move on to whatever is next.
188 if (!next_task.is_null())
189 next_task.Run();
190 }
191
StartCertificateRequest(AttestationCertificateProfile certificate_profile,const std::string & user_id,const std::string & request_origin,bool generate_new_key,const CertificateCallback & callback)192 void AttestationFlow::StartCertificateRequest(
193 AttestationCertificateProfile certificate_profile,
194 const std::string& user_id,
195 const std::string& request_origin,
196 bool generate_new_key,
197 const CertificateCallback& callback) {
198 AttestationKeyType key_type = GetKeyTypeForProfile(certificate_profile);
199 std::string key_name = GetKeyNameForProfile(certificate_profile,
200 request_origin);
201 if (generate_new_key) {
202 // Get the attestation service to create a Privacy CA certificate request.
203 async_caller_->AsyncTpmAttestationCreateCertRequest(
204 certificate_profile,
205 user_id,
206 request_origin,
207 base::Bind(&AttestationFlow::SendCertificateRequestToPCA,
208 weak_factory_.GetWeakPtr(),
209 key_type,
210 user_id,
211 key_name,
212 callback));
213 } else {
214 // If the key already exists, query the existing certificate.
215 base::Closure on_key_exists = base::Bind(
216 &AttestationFlow::GetExistingCertificate,
217 weak_factory_.GetWeakPtr(),
218 key_type,
219 user_id,
220 key_name,
221 callback);
222 // If the key does not exist, call this method back with |generate_new_key|
223 // set to true.
224 base::Closure on_key_not_exists = base::Bind(
225 &AttestationFlow::StartCertificateRequest,
226 weak_factory_.GetWeakPtr(),
227 certificate_profile,
228 user_id,
229 request_origin,
230 true,
231 callback);
232 cryptohome_client_->TpmAttestationDoesKeyExist(
233 key_type,
234 user_id,
235 key_name,
236 base::Bind(&DBusBoolRedirectCallback,
237 on_key_exists,
238 on_key_not_exists,
239 base::Bind(callback, false, "")));
240 }
241 }
242
SendCertificateRequestToPCA(AttestationKeyType key_type,const std::string & user_id,const std::string & key_name,const CertificateCallback & callback,bool success,const std::string & data)243 void AttestationFlow::SendCertificateRequestToPCA(
244 AttestationKeyType key_type,
245 const std::string& user_id,
246 const std::string& key_name,
247 const CertificateCallback& callback,
248 bool success,
249 const std::string& data) {
250 if (!success) {
251 LOG(ERROR) << "Attestation: Failed to create certificate request.";
252 if (!callback.is_null())
253 callback.Run(false, "");
254 return;
255 }
256
257 // Send the request to the Privacy CA.
258 server_proxy_->SendCertificateRequest(
259 data,
260 base::Bind(&AttestationFlow::SendCertificateResponseToDaemon,
261 weak_factory_.GetWeakPtr(),
262 key_type,
263 user_id,
264 key_name,
265 callback));
266 }
267
SendCertificateResponseToDaemon(AttestationKeyType key_type,const std::string & user_id,const std::string & key_name,const CertificateCallback & callback,bool success,const std::string & data)268 void AttestationFlow::SendCertificateResponseToDaemon(
269 AttestationKeyType key_type,
270 const std::string& user_id,
271 const std::string& key_name,
272 const CertificateCallback& callback,
273 bool success,
274 const std::string& data) {
275 if (!success) {
276 LOG(ERROR) << "Attestation: Certificate request failed.";
277 if (!callback.is_null())
278 callback.Run(false, "");
279 return;
280 }
281
282 // Forward the response to the attestation service to complete the operation.
283 async_caller_->AsyncTpmAttestationFinishCertRequest(data,
284 key_type,
285 user_id,
286 key_name,
287 base::Bind(callback));
288 }
289
GetExistingCertificate(AttestationKeyType key_type,const std::string & user_id,const std::string & key_name,const CertificateCallback & callback)290 void AttestationFlow::GetExistingCertificate(
291 AttestationKeyType key_type,
292 const std::string& user_id,
293 const std::string& key_name,
294 const CertificateCallback& callback) {
295 cryptohome_client_->TpmAttestationGetCertificate(
296 key_type,
297 user_id,
298 key_name,
299 base::Bind(&DBusDataMethodCallback, callback));
300 }
301
302 } // namespace attestation
303 } // namespace chromeos
304