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