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 <string>
6 #include <utility>
7
8 #include "base/bind.h"
9 #include "base/logging.h"
10 #include "device/fido/fido_authenticator.h"
11 #include "device/fido/fido_constants.h"
12 #include "device/fido/pin.h"
13 #include "device/fido/set_pin_request_handler.h"
14
15 namespace device {
16
SetPINRequestHandler(const base::flat_set<FidoTransportProtocol> & supported_transports,GetPINCallback get_pin_callback,FinishedCallback finished_callback,std::unique_ptr<FidoDiscoveryFactory> fido_discovery_factory)17 SetPINRequestHandler::SetPINRequestHandler(
18 const base::flat_set<FidoTransportProtocol>& supported_transports,
19 GetPINCallback get_pin_callback,
20 FinishedCallback finished_callback,
21 std::unique_ptr<FidoDiscoveryFactory> fido_discovery_factory)
22 : FidoRequestHandlerBase(fido_discovery_factory.get(),
23 supported_transports),
24 get_pin_callback_(std::move(get_pin_callback)),
25 finished_callback_(std::move(finished_callback)),
26 fido_discovery_factory_(std::move(fido_discovery_factory)) {
27 Start();
28 }
29
~SetPINRequestHandler()30 SetPINRequestHandler::~SetPINRequestHandler() {
31 DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
32 }
33
ProvidePIN(const std::string & old_pin,const std::string & new_pin)34 void SetPINRequestHandler::ProvidePIN(const std::string& old_pin,
35 const std::string& new_pin) {
36 DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
37 DCHECK_EQ(State::kWaitingForPIN, state_);
38 DCHECK(pin::IsValid(new_pin));
39
40 if (authenticator_ == nullptr) {
41 // Authenticator was detached.
42 state_ = State::kFinished;
43 finished_callback_.Run(CtapDeviceResponseCode::kCtap1ErrInvalidChannel);
44 return;
45 }
46
47 state_ = State::kSettingPIN;
48
49 if (old_pin.empty()) {
50 authenticator_->SetPIN(
51 new_pin, base::BindOnce(&SetPINRequestHandler::OnSetPINComplete,
52 weak_factory_.GetWeakPtr()));
53 } else {
54 authenticator_->ChangePIN(
55 old_pin, new_pin,
56 base::BindOnce(&SetPINRequestHandler::OnSetPINComplete,
57 weak_factory_.GetWeakPtr()));
58 }
59 }
60
DispatchRequest(FidoAuthenticator * authenticator)61 void SetPINRequestHandler::DispatchRequest(FidoAuthenticator* authenticator) {
62 DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
63
64 authenticator->GetTouch(base::BindOnce(&SetPINRequestHandler::OnTouch,
65 weak_factory_.GetWeakPtr(),
66 authenticator));
67 }
68
AuthenticatorRemoved(FidoDiscoveryBase * discovery,FidoAuthenticator * authenticator)69 void SetPINRequestHandler::AuthenticatorRemoved(
70 FidoDiscoveryBase* discovery,
71 FidoAuthenticator* authenticator) {
72 DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
73
74 if (authenticator == authenticator_) {
75 authenticator_ = nullptr;
76 }
77
78 FidoRequestHandlerBase::AuthenticatorRemoved(discovery, authenticator);
79 }
80
OnTouch(FidoAuthenticator * authenticator)81 void SetPINRequestHandler::OnTouch(FidoAuthenticator* authenticator) {
82 DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
83
84 if (state_ != State::kWaitingForTouch) {
85 return;
86 }
87
88 authenticator_ = authenticator;
89
90 switch (authenticator_->Options()->client_pin_availability) {
91 case AuthenticatorSupportedOptions::ClientPinAvailability::kNotSupported:
92 state_ = State::kFinished;
93 CancelActiveAuthenticators(authenticator->GetId());
94 finished_callback_.Run(CtapDeviceResponseCode::kCtap1ErrInvalidCommand);
95 return;
96
97 case AuthenticatorSupportedOptions::ClientPinAvailability::
98 kSupportedAndPinSet:
99 state_ = State::kGettingRetries;
100 CancelActiveAuthenticators(authenticator->GetId());
101 authenticator_->GetPinRetries(
102 base::BindOnce(&SetPINRequestHandler::OnRetriesResponse,
103 weak_factory_.GetWeakPtr()));
104 break;
105
106 case AuthenticatorSupportedOptions::ClientPinAvailability::
107 kSupportedButPinNotSet:
108 state_ = State::kWaitingForPIN;
109 CancelActiveAuthenticators(authenticator->GetId());
110 std::move(get_pin_callback_).Run(base::nullopt);
111 break;
112 }
113 }
114
OnRetriesResponse(CtapDeviceResponseCode status,base::Optional<pin::RetriesResponse> response)115 void SetPINRequestHandler::OnRetriesResponse(
116 CtapDeviceResponseCode status,
117 base::Optional<pin::RetriesResponse> response) {
118 DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
119 DCHECK_EQ(state_, State::kGettingRetries);
120
121 if (status != CtapDeviceResponseCode::kSuccess) {
122 state_ = State::kFinished;
123 finished_callback_.Run(status);
124 return;
125 }
126
127 state_ = State::kWaitingForPIN;
128 std::move(get_pin_callback_).Run(response->retries);
129 }
130
OnSetPINComplete(CtapDeviceResponseCode status,base::Optional<pin::EmptyResponse> response)131 void SetPINRequestHandler::OnSetPINComplete(
132 CtapDeviceResponseCode status,
133 base::Optional<pin::EmptyResponse> response) {
134 DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
135 DCHECK_EQ(state_, State::kSettingPIN);
136
137 if (status == CtapDeviceResponseCode::kCtap2ErrPinInvalid) {
138 // The caller may try again.
139 state_ = State::kWaitingForPIN;
140 } else {
141 state_ = State::kFinished;
142 }
143
144 finished_callback_.Run(status);
145 }
146
147 } // namespace device
148