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