1 // Copyright 2019 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 "device/fido/bio/enrollment_handler.h"
6 
7 #include "base/bind.h"
8 #include "base/callback_helpers.h"
9 #include "components/device_event_log/device_event_log.h"
10 #include "device/fido/fido_authenticator.h"
11 #include "device/fido/fido_constants.h"
12 #include "device/fido/pin.h"
13 
14 namespace device {
15 
BioEnrollmentHandler(const base::flat_set<FidoTransportProtocol> & supported_transports,base::OnceClosure ready_callback,ErrorCallback error_callback,GetPINCallback get_pin_callback,FidoDiscoveryFactory * factory)16 BioEnrollmentHandler::BioEnrollmentHandler(
17     const base::flat_set<FidoTransportProtocol>& supported_transports,
18     base::OnceClosure ready_callback,
19     ErrorCallback error_callback,
20     GetPINCallback get_pin_callback,
21     FidoDiscoveryFactory* factory)
22     : FidoRequestHandlerBase(factory, supported_transports),
23       ready_callback_(std::move(ready_callback)),
24       error_callback_(std::move(error_callback)),
25       get_pin_callback_(std::move(get_pin_callback)) {
26   Start();
27 }
28 
~BioEnrollmentHandler()29 BioEnrollmentHandler::~BioEnrollmentHandler() {
30   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
31 }
32 
EnrollTemplate(SampleCallback sample_callback,CompletionCallback completion_callback)33 void BioEnrollmentHandler::EnrollTemplate(
34     SampleCallback sample_callback,
35     CompletionCallback completion_callback) {
36   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
37   DCHECK_EQ(state_, State::kReady);
38   state_ = State::kEnrolling;
39   sample_callback_ = std::move(sample_callback);
40   enrollment_callback_ = std::move(completion_callback);
41   bio_enroller_ =
42       std::make_unique<BioEnroller>(this, authenticator_, *pin_token_response_);
43 }
44 
CancelEnrollment()45 void BioEnrollmentHandler::CancelEnrollment() {
46   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
47   DCHECK_EQ(state_, State::kEnrolling);
48   state_ = State::kCancellingEnrollment;
49   bio_enroller_->Cancel();
50 }
51 
EnumerateTemplates(EnumerationCallback callback)52 void BioEnrollmentHandler::EnumerateTemplates(EnumerationCallback callback) {
53   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
54   DCHECK(pin_token_response_);
55   DCHECK_EQ(state_, State::kReady);
56   state_ = State::kEnumerating;
57   authenticator_->BioEnrollEnumerate(
58       *pin_token_response_,
59       base::BindOnce(&BioEnrollmentHandler::OnEnumerateTemplates,
60                      weak_factory_.GetWeakPtr(), std::move(callback)));
61 }
62 
RenameTemplate(std::vector<uint8_t> template_id,std::string name,StatusCallback callback)63 void BioEnrollmentHandler::RenameTemplate(std::vector<uint8_t> template_id,
64                                           std::string name,
65                                           StatusCallback callback) {
66   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
67   DCHECK_EQ(state_, State::kReady);
68   state_ = State::kRenaming;
69   authenticator_->BioEnrollRename(
70       *pin_token_response_, std::move(template_id), std::move(name),
71       base::BindOnce(&BioEnrollmentHandler::OnRenameTemplate,
72                      weak_factory_.GetWeakPtr(), std::move(callback)));
73 }
74 
DeleteTemplate(std::vector<uint8_t> template_id,StatusCallback callback)75 void BioEnrollmentHandler::DeleteTemplate(std::vector<uint8_t> template_id,
76                                           StatusCallback callback) {
77   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
78   DCHECK_EQ(state_, State::kReady);
79   state_ = State::kDeleting;
80   authenticator_->BioEnrollDelete(
81       *pin_token_response_, std::move(template_id),
82       base::BindOnce(&BioEnrollmentHandler::OnDeleteTemplate,
83                      weak_factory_.GetWeakPtr(), std::move(callback)));
84 }
85 
DispatchRequest(FidoAuthenticator * authenticator)86 void BioEnrollmentHandler::DispatchRequest(FidoAuthenticator* authenticator) {
87   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
88   if (state_ != State::kWaitingForTouch) {
89     return;
90   }
91   authenticator->GetTouch(base::BindOnce(&BioEnrollmentHandler::OnTouch,
92                                          weak_factory_.GetWeakPtr(),
93                                          authenticator));
94 }
95 
AuthenticatorRemoved(FidoDiscoveryBase * discovery,FidoAuthenticator * authenticator)96 void BioEnrollmentHandler::AuthenticatorRemoved(
97     FidoDiscoveryBase* discovery,
98     FidoAuthenticator* authenticator) {
99   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
100   FidoRequestHandlerBase::AuthenticatorRemoved(discovery, authenticator);
101   if (authenticator_ != authenticator || state_ == State::kFinished) {
102     return;
103   }
104 
105   authenticator_ = nullptr;
106   Finish(BioEnrollmentStatus::kSuccess);
107 }
108 
OnSampleCollected(BioEnrollmentSampleStatus status,int samples_remaining)109 void BioEnrollmentHandler::OnSampleCollected(BioEnrollmentSampleStatus status,
110                                              int samples_remaining) {
111   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
112   DCHECK_EQ(state_, State::kEnrolling);
113   sample_callback_.Run(status, samples_remaining);
114 }
115 
OnEnrollmentDone(base::Optional<std::vector<uint8_t>> template_id)116 void BioEnrollmentHandler::OnEnrollmentDone(
117     base::Optional<std::vector<uint8_t>> template_id) {
118   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
119 
120   bio_enroller_.reset();
121   if (!template_id) {
122     DCHECK_EQ(state_, State::kCancellingEnrollment);
123     state_ = State::kReady;
124     std::move(enrollment_callback_)
125         .Run(CtapDeviceResponseCode::kCtap2ErrKeepAliveCancel, {});
126     return;
127   }
128   DCHECK(state_ == State::kEnrolling || state_ == State::kCancellingEnrollment);
129   state_ = State::kReady;
130   std::move(enrollment_callback_)
131       .Run(CtapDeviceResponseCode::kSuccess, std::move(*template_id));
132 }
133 
OnEnrollmentError(CtapDeviceResponseCode status)134 void BioEnrollmentHandler::OnEnrollmentError(CtapDeviceResponseCode status) {
135   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
136   DCHECK(state_ == State::kEnrolling || state_ == State::kCancellingEnrollment);
137   bio_enroller_.reset();
138   state_ = State::kReady;
139   std::move(enrollment_callback_).Run(status, {});
140 }
141 
OnTouch(FidoAuthenticator * authenticator)142 void BioEnrollmentHandler::OnTouch(FidoAuthenticator* authenticator) {
143   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
144   if (state_ != State::kWaitingForTouch) {
145     return;
146   }
147 
148   CancelActiveAuthenticators(authenticator->GetId());
149 
150   if (!authenticator->Options() ||
151       (authenticator->Options()->bio_enrollment_availability ==
152            AuthenticatorSupportedOptions::BioEnrollmentAvailability::
153                kNotSupported &&
154        authenticator->Options()->bio_enrollment_availability_preview ==
155            AuthenticatorSupportedOptions::BioEnrollmentAvailability::
156                kNotSupported)) {
157     Finish(BioEnrollmentStatus::kAuthenticatorMissingBioEnrollment);
158     return;
159   }
160 
161   if (authenticator->Options()->client_pin_availability !=
162       AuthenticatorSupportedOptions::ClientPinAvailability::
163           kSupportedAndPinSet) {
164     Finish(BioEnrollmentStatus::kNoPINSet);
165     return;
166   }
167 
168   authenticator_ = authenticator;
169   state_ = State::kGettingRetries;
170   authenticator_->GetPinRetries(base::BindOnce(
171       &BioEnrollmentHandler::OnRetriesResponse, weak_factory_.GetWeakPtr()));
172 }
173 
OnRetriesResponse(CtapDeviceResponseCode status,base::Optional<pin::RetriesResponse> response)174 void BioEnrollmentHandler::OnRetriesResponse(
175     CtapDeviceResponseCode status,
176     base::Optional<pin::RetriesResponse> response) {
177   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
178   DCHECK_EQ(state_, State::kGettingRetries);
179   if (!response || status != CtapDeviceResponseCode::kSuccess) {
180     Finish(BioEnrollmentStatus::kAuthenticatorResponseInvalid);
181     return;
182   }
183 
184   if (response->retries == 0) {
185     Finish(BioEnrollmentStatus::kHardPINBlock);
186     return;
187   }
188 
189   state_ = State::kWaitingForPIN;
190   get_pin_callback_.Run(response->retries,
191                         base::BindOnce(&BioEnrollmentHandler::OnHavePIN,
192                                        weak_factory_.GetWeakPtr()));
193 }
194 
OnHavePIN(std::string pin)195 void BioEnrollmentHandler::OnHavePIN(std::string pin) {
196   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
197   DCHECK_EQ(state_, State::kWaitingForPIN);
198   state_ = State::kGettingPINToken;
199   authenticator_->GetPINToken(
200       std::move(pin), {pin::Permissions::kBioEnrollment},
201       /*rp_id=*/base::nullopt,
202       base::BindOnce(&BioEnrollmentHandler::OnHavePINToken,
203                      weak_factory_.GetWeakPtr()));
204 }
205 
OnHavePINToken(CtapDeviceResponseCode status,base::Optional<pin::TokenResponse> response)206 void BioEnrollmentHandler::OnHavePINToken(
207     CtapDeviceResponseCode status,
208     base::Optional<pin::TokenResponse> response) {
209   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
210   DCHECK_EQ(state_, State::kGettingPINToken);
211 
212   if (status != CtapDeviceResponseCode::kSuccess) {
213     switch (status) {
214       case CtapDeviceResponseCode::kCtap2ErrPinInvalid:
215         state_ = State::kGettingRetries;
216         authenticator_->GetPinRetries(
217             base::BindOnce(&BioEnrollmentHandler::OnRetriesResponse,
218                            weak_factory_.GetWeakPtr()));
219         return;
220       case CtapDeviceResponseCode::kCtap2ErrPinAuthBlocked:
221         Finish(BioEnrollmentStatus::kSoftPINBlock);
222         return;
223       case CtapDeviceResponseCode::kCtap2ErrPinBlocked:
224         Finish(BioEnrollmentStatus::kHardPINBlock);
225         return;
226       default:
227         Finish(BioEnrollmentStatus::kAuthenticatorResponseInvalid);
228         return;
229     }
230   }
231 
232   state_ = State::kReady;
233   pin_token_response_ = std::move(response);
234   std::move(ready_callback_).Run();
235 }
236 
OnEnumerateTemplates(EnumerationCallback callback,CtapDeviceResponseCode status,base::Optional<BioEnrollmentResponse> response)237 void BioEnrollmentHandler::OnEnumerateTemplates(
238     EnumerationCallback callback,
239     CtapDeviceResponseCode status,
240     base::Optional<BioEnrollmentResponse> response) {
241   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
242   DCHECK_EQ(state_, State::kEnumerating);
243 
244   state_ = State::kReady;
245 
246   if (status != CtapDeviceResponseCode::kSuccess) {
247     std::move(callback).Run(status, base::nullopt);
248     return;
249   }
250 
251   if (!response || !response->template_infos) {
252     Finish(BioEnrollmentStatus::kAuthenticatorResponseInvalid);
253     return;
254   }
255 
256   std::move(callback).Run(status, std::move(*response->template_infos));
257 }
258 
OnRenameTemplate(StatusCallback callback,CtapDeviceResponseCode status,base::Optional<BioEnrollmentResponse> response)259 void BioEnrollmentHandler::OnRenameTemplate(
260     StatusCallback callback,
261     CtapDeviceResponseCode status,
262     base::Optional<BioEnrollmentResponse> response) {
263   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
264   DCHECK_EQ(state_, State::kRenaming);
265   state_ = State::kReady;
266   std::move(callback).Run(status);
267 }
268 
OnDeleteTemplate(StatusCallback callback,CtapDeviceResponseCode status,base::Optional<BioEnrollmentResponse> response)269 void BioEnrollmentHandler::OnDeleteTemplate(
270     StatusCallback callback,
271     CtapDeviceResponseCode status,
272     base::Optional<BioEnrollmentResponse> response) {
273   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
274   DCHECK_EQ(state_, State::kDeleting);
275   state_ = State::kReady;
276   std::move(callback).Run(status);
277 }
278 
Finish(BioEnrollmentStatus status)279 void BioEnrollmentHandler::Finish(BioEnrollmentStatus status) {
280   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
281   DCHECK_NE(state_, State::kFinished);
282   state_ = State::kFinished;
283   std::move(error_callback_).Run(status);
284 }
285 
286 }  // namespace device
287