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