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 <algorithm>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/compiler_specific.h"
12 #include "base/logging.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/optional.h"
15 #include "base/threading/thread_task_runner_handle.h"
16 #include "base/timer/timer.h"
17 #include "chromeos/attestation/attestation_flow_utils.h"
18 #include "chromeos/cryptohome/cryptohome_parameters.h"
19 #include "chromeos/dbus/attestation/attestation_client.h"
20 #include "chromeos/dbus/attestation/interface.pb.h"
21 #include "components/account_id/account_id.h"
22
23 namespace chromeos {
24 namespace attestation {
25
26 namespace {
27
ProfileToAttestationProtoEnum(AttestationCertificateProfile p)28 base::Optional<::attestation::CertificateProfile> ProfileToAttestationProtoEnum(
29 AttestationCertificateProfile p) {
30 switch (p) {
31 case PROFILE_ENTERPRISE_MACHINE_CERTIFICATE:
32 return ::attestation::CertificateProfile::ENTERPRISE_MACHINE_CERTIFICATE;
33 case PROFILE_ENTERPRISE_USER_CERTIFICATE:
34 return ::attestation::CertificateProfile::ENTERPRISE_USER_CERTIFICATE;
35 case PROFILE_CONTENT_PROTECTION_CERTIFICATE:
36 return ::attestation::CertificateProfile::CONTENT_PROTECTION_CERTIFICATE;
37 case PROFILE_ENTERPRISE_ENROLLMENT_CERTIFICATE:
38 return ::attestation::CertificateProfile::
39 ENTERPRISE_ENROLLMENT_CERTIFICATE;
40 }
41 return {};
42 }
43
ToAcaType(PrivacyCAType type)44 ::attestation::ACAType ToAcaType(PrivacyCAType type) {
45 switch (type) {
46 case DEFAULT_PCA:
47 return ::attestation::DEFAULT_ACA;
48 case TEST_PCA:
49 return ::attestation::TEST_ACA;
50 }
51 LOG(DFATAL) << "Unknown type to convert: " << type;
52 return ::attestation::DEFAULT_ACA;
53 }
54
55 // A reasonable timeout that gives enough time for attestation to be ready,
56 // yet does not make the caller wait too long.
57 constexpr uint16_t kReadyTimeoutInSeconds = 60;
58
59 // Delay before checking again whether the TPM has been prepared for
60 // attestation.
61 constexpr uint16_t kRetryDelayInMilliseconds = 300;
62
63 } // namespace
64
GetKeyTypeForProfile(AttestationCertificateProfile certificate_profile)65 AttestationKeyType AttestationFlow::GetKeyTypeForProfile(
66 AttestationCertificateProfile certificate_profile) {
67 switch (certificate_profile) {
68 case PROFILE_ENTERPRISE_MACHINE_CERTIFICATE:
69 case PROFILE_ENTERPRISE_ENROLLMENT_CERTIFICATE:
70 return KEY_DEVICE;
71 case PROFILE_ENTERPRISE_USER_CERTIFICATE:
72 case PROFILE_CONTENT_PROTECTION_CERTIFICATE:
73 return KEY_USER;
74 }
75 NOTREACHED();
76 return KEY_USER;
77 }
78
AttestationFlow(std::unique_ptr<ServerProxy> server_proxy,::attestation::KeyType crypto_key_type)79 AttestationFlow::AttestationFlow(std::unique_ptr<ServerProxy> server_proxy,
80 ::attestation::KeyType crypto_key_type)
81 : attestation_client_(AttestationClient::Get()),
82 server_proxy_(std::move(server_proxy)),
83 crypto_key_type_(crypto_key_type),
84 ready_timeout_(base::TimeDelta::FromSeconds(kReadyTimeoutInSeconds)),
85 retry_delay_(
86 base::TimeDelta::FromMilliseconds(kRetryDelayInMilliseconds)) {}
87
AttestationFlow(std::unique_ptr<ServerProxy> server_proxy)88 AttestationFlow::AttestationFlow(std::unique_ptr<ServerProxy> server_proxy)
89 : AttestationFlow(std::move(server_proxy), ::attestation::KEY_TYPE_RSA) {}
90
91 AttestationFlow::~AttestationFlow() = default;
92
GetCertificate(AttestationCertificateProfile certificate_profile,const AccountId & account_id,const std::string & request_origin,bool force_new_key,const std::string & key_name,CertificateCallback callback)93 void AttestationFlow::GetCertificate(
94 AttestationCertificateProfile certificate_profile,
95 const AccountId& account_id,
96 const std::string& request_origin,
97 bool force_new_key,
98 const std::string& key_name,
99 CertificateCallback callback) {
100 std::string attestation_key_name =
101 !key_name.empty()
102 ? key_name
103 : GetKeyNameForProfile(certificate_profile, request_origin);
104
105 base::OnceCallback<void(bool)> start_certificate_request = base::BindOnce(
106 &AttestationFlow::StartCertificateRequest, weak_factory_.GetWeakPtr(),
107 certificate_profile, account_id, request_origin, force_new_key,
108 attestation_key_name, std::move(callback));
109
110 // If this device has not enrolled with the Privacy CA, we need to do that
111 // first. Once enrolled we can proceed with the certificate request.
112 attestation_client_->GetStatus(
113 ::attestation::GetStatusRequest(),
114 base::BindOnce(&AttestationFlow::OnEnrollmentCheckComplete,
115 weak_factory_.GetWeakPtr(),
116 std::move(start_certificate_request)));
117 }
118
OnEnrollmentCheckComplete(base::OnceCallback<void (bool)> callback,const::attestation::GetStatusReply & reply)119 void AttestationFlow::OnEnrollmentCheckComplete(
120 base::OnceCallback<void(bool)> callback,
121 const ::attestation::GetStatusReply& reply) {
122 if (reply.status() != ::attestation::STATUS_SUCCESS) {
123 LOG(ERROR) << "Attestation: Failed to check enrollment state. Status: "
124 << reply.status();
125 std::move(callback).Run(false);
126 return;
127 }
128
129 if (reply.enrolled()) {
130 std::move(callback).Run(true);
131 return;
132 }
133
134 // The device is not enrolled; check if it's enrollment prepared.
135 base::TimeTicks end_time = base::TimeTicks::Now() + ready_timeout_;
136 WaitForAttestationPrepared(end_time, std::move(callback));
137 }
138
WaitForAttestationPrepared(base::TimeTicks end_time,base::OnceCallback<void (bool)> callback)139 void AttestationFlow::WaitForAttestationPrepared(
140 base::TimeTicks end_time,
141 base::OnceCallback<void(bool)> callback) {
142 ::attestation::GetEnrollmentPreparationsRequest request;
143 attestation_client_->GetEnrollmentPreparations(
144 request, base::BindOnce(&AttestationFlow::OnPreparedCheckComplete,
145 weak_factory_.GetWeakPtr(), end_time,
146 std::move(callback)));
147 }
148
OnPreparedCheckComplete(base::TimeTicks end_time,base::OnceCallback<void (bool)> callback,const::attestation::GetEnrollmentPreparationsReply & reply)149 void AttestationFlow::OnPreparedCheckComplete(
150 base::TimeTicks end_time,
151 base::OnceCallback<void(bool)> callback,
152 const ::attestation::GetEnrollmentPreparationsReply& reply) {
153 if (AttestationClient::IsAttestationPrepared(reply)) {
154 // Get the attestation service to create a Privacy CA enrollment request.
155 ::attestation::CreateEnrollRequestRequest request;
156 request.set_aca_type(ToAcaType(server_proxy_->GetType()));
157 AttestationClient::Get()->CreateEnrollRequest(
158 request,
159 base::BindOnce(&AttestationFlow::SendEnrollRequestToPCA,
160 weak_factory_.GetWeakPtr(), std::move(callback)));
161 return;
162 }
163
164 if (base::TimeTicks::Now() < end_time) {
165 LOG(WARNING) << "Attestation: Not prepared yet."
166 << " Retrying in " << retry_delay_ << ".";
167 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
168 FROM_HERE,
169 base::BindOnce(&AttestationFlow::WaitForAttestationPrepared,
170 weak_factory_.GetWeakPtr(), end_time,
171 std::move(callback)),
172 retry_delay_);
173 return;
174 }
175
176 LOG(ERROR) << "Attestation: Not prepared. Giving up on retrying.";
177 std::move(callback).Run(false);
178 }
179
SendEnrollRequestToPCA(base::OnceCallback<void (bool)> callback,const::attestation::CreateEnrollRequestReply & reply)180 void AttestationFlow::SendEnrollRequestToPCA(
181 base::OnceCallback<void(bool)> callback,
182 const ::attestation::CreateEnrollRequestReply& reply) {
183 if (reply.status() != ::attestation::STATUS_SUCCESS) {
184 LOG(ERROR) << "Attestation: Failed to create enroll request; status: "
185 << reply.status();
186 std::move(callback).Run(false);
187 return;
188 }
189
190 // Send the request to the Privacy CA.
191 server_proxy_->SendEnrollRequest(
192 reply.pca_request(),
193 base::BindOnce(&AttestationFlow::SendEnrollResponseToDaemon,
194 weak_factory_.GetWeakPtr(), std::move(callback)));
195 }
196
SendEnrollResponseToDaemon(base::OnceCallback<void (bool)> callback,bool success,const std::string & data)197 void AttestationFlow::SendEnrollResponseToDaemon(
198 base::OnceCallback<void(bool)> callback,
199 bool success,
200 const std::string& data) {
201 if (!success) {
202 LOG(ERROR) << "Attestation: Enroll request failed.";
203 std::move(callback).Run(false);
204 return;
205 }
206
207 // Forward the response to the attestation service to complete enrollment.
208 ::attestation::FinishEnrollRequest request;
209 request.set_pca_response(data);
210 request.set_aca_type(ToAcaType(server_proxy_->GetType()));
211 AttestationClient::Get()->FinishEnroll(
212 request, base::BindOnce(&AttestationFlow::OnEnrollComplete,
213 weak_factory_.GetWeakPtr(), std::move(callback)));
214 }
215
OnEnrollComplete(base::OnceCallback<void (bool)> callback,const::attestation::FinishEnrollReply & reply)216 void AttestationFlow::OnEnrollComplete(
217 base::OnceCallback<void(bool)> callback,
218 const ::attestation::FinishEnrollReply& reply) {
219 if (reply.status() != ::attestation::STATUS_SUCCESS) {
220 LOG(ERROR) << "Attestation: Failed to complete enrollment; status: "
221 << reply.status();
222 std::move(callback).Run(false);
223 return;
224 }
225
226 std::move(callback).Run(true);
227 }
228
StartCertificateRequest(AttestationCertificateProfile certificate_profile,const AccountId & account_id,const std::string & request_origin,bool generate_new_key,const std::string & key_name,CertificateCallback callback,bool enrolled)229 void AttestationFlow::StartCertificateRequest(
230 AttestationCertificateProfile certificate_profile,
231 const AccountId& account_id,
232 const std::string& request_origin,
233 bool generate_new_key,
234 const std::string& key_name,
235 CertificateCallback callback,
236 bool enrolled) {
237 if (!enrolled) {
238 std::move(callback).Run(ATTESTATION_UNSPECIFIED_FAILURE, "");
239 return;
240 }
241
242 AttestationKeyType key_type = GetKeyTypeForProfile(certificate_profile);
243 if (generate_new_key) {
244 // Get the attestation service to create a Privacy CA certificate request.
245 const base::Optional<::attestation::CertificateProfile>
246 attestation_profile =
247 ProfileToAttestationProtoEnum(certificate_profile);
248 if (!attestation_profile) {
249 LOG(DFATAL) << "Attestation: Unrecognized profile type: "
250 << certificate_profile;
251 return;
252 }
253
254 ::attestation::CreateCertificateRequestRequest request;
255 if (key_type == KEY_USER) {
256 request.set_username(cryptohome::Identification(account_id).id());
257 }
258 request.set_certificate_profile(*attestation_profile);
259 request.set_request_origin(request_origin);
260 request.set_key_type(crypto_key_type_);
261
262 attestation_client_->CreateCertificateRequest(
263 request, base::BindOnce(&AttestationFlow::SendCertificateRequestToPCA,
264 weak_factory_.GetWeakPtr(), key_type,
265 account_id, key_name, std::move(callback)));
266 return;
267 }
268
269 ::attestation::GetKeyInfoRequest request;
270 if (key_type == KEY_USER) {
271 request.set_username(cryptohome::Identification(account_id).id());
272 }
273 request.set_key_label(key_name);
274 attestation_client_->GetKeyInfo(
275 request, base::BindOnce(&AttestationFlow::OnGetKeyInfoComplete,
276 weak_factory_.GetWeakPtr(), certificate_profile,
277 account_id, request_origin, key_name, key_type,
278 std::move(callback)));
279 }
280
OnGetKeyInfoComplete(AttestationCertificateProfile certificate_profile,const AccountId & account_id,const std::string & request_origin,const std::string & key_name,AttestationKeyType key_type,CertificateCallback callback,const::attestation::GetKeyInfoReply & reply)281 void AttestationFlow::OnGetKeyInfoComplete(
282 AttestationCertificateProfile certificate_profile,
283 const AccountId& account_id,
284 const std::string& request_origin,
285 const std::string& key_name,
286 AttestationKeyType key_type,
287 CertificateCallback callback,
288 const ::attestation::GetKeyInfoReply& reply) {
289 // If the key already exists, return the existing certificate.
290 if (reply.status() == ::attestation::STATUS_SUCCESS) {
291 std::move(callback).Run(ATTESTATION_SUCCESS, reply.certificate());
292 return;
293 }
294
295 // If the key does not exist, call this method back with |generate_new_key|
296 // set to true.
297 if (reply.status() == ::attestation::STATUS_INVALID_PARAMETER) {
298 StartCertificateRequest(certificate_profile, account_id, request_origin,
299 /*generate_new_key=*/true, key_name,
300 std::move(callback), /*enrolled=*/true);
301 return;
302 }
303
304 // Otherwise the key info query fails.
305 LOG(ERROR) << "Attestation: Failed to check for existence of key; status: "
306 << reply.status() << ".";
307 std::move(callback).Run(ATTESTATION_UNSPECIFIED_FAILURE, "");
308 }
309
SendCertificateRequestToPCA(AttestationKeyType key_type,const AccountId & account_id,const std::string & key_name,CertificateCallback callback,const::attestation::CreateCertificateRequestReply & reply)310 void AttestationFlow::SendCertificateRequestToPCA(
311 AttestationKeyType key_type,
312 const AccountId& account_id,
313 const std::string& key_name,
314 CertificateCallback callback,
315 const ::attestation::CreateCertificateRequestReply& reply) {
316 if (reply.status() != ::attestation::STATUS_SUCCESS) {
317 LOG(ERROR) << "Attestation: Failed to create certificate request. Status: "
318 << reply.status();
319 std::move(callback).Run(ATTESTATION_UNSPECIFIED_FAILURE, "");
320 return;
321 }
322
323 // Send the request to the Privacy CA.
324 server_proxy_->SendCertificateRequest(
325 reply.pca_request(),
326 base::BindOnce(&AttestationFlow::SendCertificateResponseToDaemon,
327 weak_factory_.GetWeakPtr(), key_type, account_id, key_name,
328 std::move(callback)));
329 }
330
SendCertificateResponseToDaemon(AttestationKeyType key_type,const AccountId & account_id,const std::string & key_name,CertificateCallback callback,bool success,const std::string & data)331 void AttestationFlow::SendCertificateResponseToDaemon(
332 AttestationKeyType key_type,
333 const AccountId& account_id,
334 const std::string& key_name,
335 CertificateCallback callback,
336 bool success,
337 const std::string& data) {
338 if (!success) {
339 LOG(ERROR) << "Attestation: Certificate request failed.";
340 std::move(callback).Run(ATTESTATION_UNSPECIFIED_FAILURE, "");
341 return;
342 }
343
344 ::attestation::FinishCertificateRequestRequest request;
345 if (key_type == KEY_USER) {
346 request.set_username(cryptohome::Identification(account_id).id());
347 }
348 request.set_key_label(key_name);
349 request.set_pca_response(data);
350 AttestationClient::Get()->FinishCertificateRequest(
351 request, base::BindOnce(&AttestationFlow::OnCertRequestFinished,
352 weak_factory_.GetWeakPtr(), std::move(callback)));
353 }
354
OnCertRequestFinished(CertificateCallback callback,const::attestation::FinishCertificateRequestReply & reply)355 void AttestationFlow::OnCertRequestFinished(
356 CertificateCallback callback,
357 const ::attestation::FinishCertificateRequestReply& reply) {
358 if (reply.status() == ::attestation::STATUS_SUCCESS) {
359 std::move(callback).Run(ATTESTATION_SUCCESS, reply.certificate());
360 } else {
361 LOG(ERROR) << "Failed to finish certificate request; status: "
362 << reply.status();
363 std::move(callback).Run(ATTESTATION_SERVER_BAD_REQUEST_FAILURE,
364 /*pem_certificate_chain=*/"");
365 }
366 }
367
368 ServerProxy::~ServerProxy() = default;
369
GetType()370 PrivacyCAType ServerProxy::GetType() {
371 return DEFAULT_PCA;
372 }
373
374 } // namespace attestation
375 } // namespace chromeos
376