get_assertion_request_handler.cc 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849
  1. // Copyright 2018 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. #include "device/fido/get_assertion_request_handler.h"
  5. #include <set>
  6. #include <string>
  7. #include <utility>
  8. #include <vector>
  9. #include "base/bind.h"
  10. #include "base/containers/cxx20_erase.h"
  11. #include "base/metrics/histogram_functions.h"
  12. #include "base/ranges/algorithm.h"
  13. #include "base/stl_util.h"
  14. #include "base/strings/string_number_conversions.h"
  15. #include "base/timer/elapsed_timer.h"
  16. #include "build/build_config.h"
  17. #include "build/chromeos_buildflags.h"
  18. #include "components/cbor/diagnostic_writer.h"
  19. #include "components/device_event_log/device_event_log.h"
  20. #include "device/fido/cable/fido_cable_discovery.h"
  21. #include "device/fido/discoverable_credential_metadata.h"
  22. #include "device/fido/features.h"
  23. #include "device/fido/fido_authenticator.h"
  24. #include "device/fido/fido_discovery_factory.h"
  25. #include "device/fido/fido_parsing_utils.h"
  26. #include "device/fido/filter.h"
  27. #include "device/fido/get_assertion_task.h"
  28. #include "device/fido/large_blob.h"
  29. #include "device/fido/pin.h"
  30. #if BUILDFLAG(IS_MAC)
  31. #include "device/fido/mac/authenticator.h"
  32. #endif // BUILDFLAG(IS_MAC)
  33. #if BUILDFLAG(IS_WIN)
  34. #include "device/fido/win/authenticator.h"
  35. #include "device/fido/win/type_conversions.h"
  36. #endif
  37. #if BUILDFLAG(IS_CHROMEOS)
  38. #include "device/fido/cros/authenticator.h"
  39. #endif
  40. namespace device {
  41. namespace {
  42. using PINUVDisposition = FidoAuthenticator::PINUVDisposition;
  43. const std::set<pin::Permissions> GetPinTokenPermissionsFor(
  44. const FidoAuthenticator& authenticator,
  45. const CtapGetAssertionRequest& request) {
  46. std::set<pin::Permissions> permissions = {pin::Permissions::kGetAssertion};
  47. if (request.large_blob_write && authenticator.Options() &&
  48. authenticator.Options()->supports_large_blobs) {
  49. permissions.emplace(pin::Permissions::kLargeBlobWrite);
  50. }
  51. return permissions;
  52. }
  53. absl::optional<GetAssertionStatus> ConvertDeviceResponseCode(
  54. CtapDeviceResponseCode device_response_code) {
  55. switch (device_response_code) {
  56. case CtapDeviceResponseCode::kSuccess:
  57. return GetAssertionStatus::kSuccess;
  58. // Only returned after the user interacted with the
  59. // authenticator.
  60. case CtapDeviceResponseCode::kCtap2ErrNoCredentials:
  61. return GetAssertionStatus::kUserConsentButCredentialNotRecognized;
  62. // The user explicitly denied the operation. Touch ID returns this error
  63. // when the user cancels the macOS prompt. External authenticators may
  64. // return it e.g. after the user fails fingerprint verification.
  65. case CtapDeviceResponseCode::kCtap2ErrOperationDenied:
  66. return GetAssertionStatus::kUserConsentDenied;
  67. // External authenticators may return this error if internal user
  68. // verification fails or if the pin token is not valid.
  69. case CtapDeviceResponseCode::kCtap2ErrPinAuthInvalid:
  70. return GetAssertionStatus::kUserConsentDenied;
  71. // This error is returned by some authenticators (e.g. the "Yubico FIDO
  72. // 2" CTAP2 USB keys) during GetAssertion **before the user interacted
  73. // with the device**. The authenticator does this to avoid blinking (and
  74. // possibly asking the user for their PIN) for requests it knows
  75. // beforehand it cannot handle.
  76. //
  77. // Ignore this error to avoid canceling the request without user
  78. // interaction.
  79. case CtapDeviceResponseCode::kCtap2ErrInvalidCredential:
  80. return absl::nullopt;
  81. // For all other errors, the authenticator will be dropped, and other
  82. // authenticators may continue.
  83. default:
  84. return absl::nullopt;
  85. }
  86. }
  87. // ValidateResponseExtensions returns true iff |extensions| is valid as a
  88. // response to |request| and |options|.
  89. bool ValidateResponseExtensions(const CtapGetAssertionRequest& request,
  90. const CtapGetAssertionOptions& options,
  91. const cbor::Value& extensions) {
  92. if (!extensions.is_map()) {
  93. return false;
  94. }
  95. for (const auto& it : extensions.GetMap()) {
  96. if (!it.first.is_string()) {
  97. return false;
  98. }
  99. const std::string& ext_name = it.first.GetString();
  100. if (ext_name == kExtensionHmacSecret) {
  101. // This extension is checked by |GetAssertionTask| because it needs to be
  102. // decrypted there.
  103. continue;
  104. } else if (ext_name == kExtensionCredBlob) {
  105. if (!request.get_cred_blob || !it.second.is_bytestring()) {
  106. return false;
  107. }
  108. } else {
  109. // Authenticators may not return unknown extensions.
  110. return false;
  111. }
  112. }
  113. return true;
  114. }
  115. // ResponseValid returns whether |response| is permissible for the given
  116. // |authenticator| and |request|. |is_first_response| is true if this is the
  117. // first assertion response read. For responses to getNextAssertion commands
  118. // it should be false.
  119. bool ResponseValid(bool is_first_response,
  120. const FidoAuthenticator& authenticator,
  121. const CtapGetAssertionRequest& request,
  122. const CtapGetAssertionOptions& options,
  123. const AuthenticatorGetAssertionResponse& response) {
  124. // The underlying code must take care of filling in the credential from the
  125. // allow list as needed.
  126. CHECK(response.credential);
  127. const std::array<uint8_t, kRpIdHashLength>& rp_id_hash =
  128. response.authenticator_data.application_parameter();
  129. if (rp_id_hash != fido_parsing_utils::CreateSHA256Hash(request.rp_id) &&
  130. (!request.app_id ||
  131. rp_id_hash != request.alternative_application_parameter)) {
  132. return false;
  133. }
  134. // PublicKeyUserEntity field in GetAssertion response is optional with the
  135. // following constraints:
  136. // - If assertion has been made without user verification, user identifiable
  137. // information must not be included.
  138. // - For resident key credentials, user id of the user entity is mandatory.
  139. // - When multiple accounts exist for specified RP ID, user entity is
  140. // mandatory.
  141. // TODO(hongjunchoi) : Add link to section of the CTAP spec once it is
  142. // published.
  143. const auto& user_entity = response.user_entity;
  144. const bool has_user_identifying_info =
  145. user_entity &&
  146. (user_entity->display_name || user_entity->name || user_entity->icon_url);
  147. if (!response.authenticator_data.obtained_user_verification() &&
  148. has_user_identifying_info) {
  149. return false;
  150. }
  151. if (request.allow_list.empty() && !user_entity) {
  152. return false;
  153. }
  154. if (response.num_credentials.value_or(0u) > 1 && !user_entity) {
  155. return false;
  156. }
  157. // The authenticatorData on an GetAssertionResponse must not have
  158. // attestedCredentialData set.
  159. if (response.authenticator_data.attested_data().has_value()) {
  160. return false;
  161. }
  162. const absl::optional<cbor::Value>& extensions =
  163. response.authenticator_data.extensions();
  164. if (extensions &&
  165. !ValidateResponseExtensions(request, options, *extensions)) {
  166. FIDO_LOG(ERROR) << "assertion response invalid due to extensions block: "
  167. << cbor::DiagnosticWriter::Write(*extensions);
  168. return false;
  169. }
  170. if (!is_first_response && response.user_selected) {
  171. // It is invalid to set `userSelected` on subsequent responses.
  172. return false;
  173. }
  174. return true;
  175. }
  176. base::flat_set<FidoTransportProtocol> GetTransportsAllowedByRP(
  177. const CtapGetAssertionRequest& request) {
  178. const base::flat_set<FidoTransportProtocol> kAllTransports = {
  179. FidoTransportProtocol::kInternal,
  180. FidoTransportProtocol::kNearFieldCommunication,
  181. FidoTransportProtocol::kUsbHumanInterfaceDevice,
  182. FidoTransportProtocol::kBluetoothLowEnergy,
  183. FidoTransportProtocol::kHybrid,
  184. FidoTransportProtocol::kAndroidAccessory,
  185. };
  186. const auto& allowed_list = request.allow_list;
  187. if (allowed_list.empty()) {
  188. return kAllTransports;
  189. }
  190. base::flat_set<FidoTransportProtocol> transports;
  191. for (const auto& credential : allowed_list) {
  192. if (credential.transports.empty()) {
  193. return kAllTransports;
  194. }
  195. transports.insert(credential.transports.begin(),
  196. credential.transports.end());
  197. }
  198. transports.insert(device::FidoTransportProtocol::kAndroidAccessory);
  199. return transports;
  200. }
  201. void ReportGetAssertionRequestTransport(FidoAuthenticator* authenticator) {
  202. if (authenticator->AuthenticatorTransport()) {
  203. base::UmaHistogramEnumeration(
  204. "WebAuthentication.GetAssertionRequestTransport",
  205. *authenticator->AuthenticatorTransport());
  206. }
  207. }
  208. void ReportGetAssertionResponseTransport(FidoAuthenticator* authenticator) {
  209. if (authenticator->AuthenticatorTransport()) {
  210. base::UmaHistogramEnumeration(
  211. "WebAuthentication.GetAssertionResponseTransport",
  212. *authenticator->AuthenticatorTransport());
  213. }
  214. }
  215. CtapGetAssertionRequest SpecializeRequestForAuthenticator(
  216. const CtapGetAssertionRequest& request,
  217. const FidoAuthenticator& authenticator) {
  218. CtapGetAssertionRequest specialized_request(request);
  219. if (!authenticator.Options() ||
  220. !authenticator.Options()->supports_large_blobs) {
  221. // Do not attempt large blob operations on devices not supporting it.
  222. specialized_request.large_blob_key = false;
  223. specialized_request.large_blob_read = false;
  224. specialized_request.large_blob_write.reset();
  225. }
  226. if (!request.is_u2f_only && authenticator.Options() &&
  227. authenticator.Options()->always_uv) {
  228. specialized_request.user_verification =
  229. UserVerificationRequirement::kRequired;
  230. }
  231. if (request.get_cred_blob && !authenticator.SupportsCredBlobOfSize(0)) {
  232. specialized_request.get_cred_blob = false;
  233. }
  234. return specialized_request;
  235. }
  236. } // namespace
  237. GetAssertionRequestHandler::GetAssertionRequestHandler(
  238. FidoDiscoveryFactory* fido_discovery_factory,
  239. const base::flat_set<FidoTransportProtocol>& supported_transports,
  240. CtapGetAssertionRequest request,
  241. CtapGetAssertionOptions options,
  242. bool allow_skipping_pin_touch,
  243. CompletionCallback completion_callback)
  244. : FidoRequestHandlerBase(
  245. fido_discovery_factory,
  246. base::STLSetIntersection<base::flat_set<FidoTransportProtocol>>(
  247. supported_transports,
  248. GetTransportsAllowedByRP(request))),
  249. completion_callback_(std::move(completion_callback)),
  250. request_(std::move(request)),
  251. options_(std::move(options)),
  252. allow_skipping_pin_touch_(allow_skipping_pin_touch) {
  253. transport_availability_info().request_type = FidoRequestType::kGetAssertion;
  254. transport_availability_info().has_empty_allow_list =
  255. request_.allow_list.empty();
  256. transport_availability_info().is_off_the_record_context =
  257. request_.is_off_the_record_context;
  258. if (request_.allow_list.empty()) {
  259. // Resident credential requests always involve user verification.
  260. request_.user_verification = UserVerificationRequirement::kRequired;
  261. }
  262. FIDO_LOG(EVENT) << "Starting GetAssertion flow";
  263. Start();
  264. }
  265. GetAssertionRequestHandler::~GetAssertionRequestHandler() = default;
  266. void GetAssertionRequestHandler::PreselectAccount(
  267. std::vector<uint8_t> credential_id) {
  268. // PreselectAccount is only supposed to be invoked for discoverable credential
  269. // requests.
  270. DCHECK(request_.allow_list.empty());
  271. preselected_credential_ = std::move(credential_id);
  272. }
  273. base::WeakPtr<GetAssertionRequestHandler>
  274. GetAssertionRequestHandler::GetWeakPtr() {
  275. return weak_factory_.GetWeakPtr();
  276. }
  277. void GetAssertionRequestHandler::OnBluetoothAdapterEnumerated(
  278. bool is_present,
  279. bool is_powered_on,
  280. bool can_power_on,
  281. bool is_peripheral_role_supported) {
  282. if (!is_peripheral_role_supported && request_.cable_extension) {
  283. // caBLEv1 relies on the client being able to broadcast Bluetooth
  284. // advertisements. |is_peripheral_role_supported| supposedly indicates
  285. // whether the adapter supports advertising, but there appear to be false
  286. // negatives (crbug/1074692). So we can't really do anything about it
  287. // besides log it to aid diagnostics.
  288. FIDO_LOG(ERROR)
  289. << "caBLEv1 request, but BLE adapter does not support peripheral role";
  290. }
  291. FidoRequestHandlerBase::OnBluetoothAdapterEnumerated(
  292. is_present, is_powered_on, can_power_on, is_peripheral_role_supported);
  293. }
  294. void GetAssertionRequestHandler::DispatchRequest(
  295. FidoAuthenticator* authenticator) {
  296. DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
  297. if (state_ != State::kWaitingForTouch) {
  298. FIDO_LOG(DEBUG) << "Not dispatching request to "
  299. << authenticator->GetDisplayName()
  300. << " because no longer waiting for touch";
  301. return;
  302. }
  303. const std::string authenticator_name = authenticator->GetDisplayName();
  304. if (fido_filter::Evaluate(fido_filter::Operation::GET_ASSERTION,
  305. request_.rp_id, authenticator_name,
  306. absl::nullopt) == fido_filter::Action::BLOCK) {
  307. FIDO_LOG(DEBUG) << "Filtered request to device " << authenticator_name;
  308. return;
  309. }
  310. for (const auto& cred : request_.allow_list) {
  311. if (fido_filter::Evaluate(
  312. fido_filter::Operation::GET_ASSERTION, request_.rp_id,
  313. authenticator_name,
  314. std::pair<fido_filter::IDType, base::span<const uint8_t>>(
  315. fido_filter::IDType::CREDENTIAL_ID, cred.id)) ==
  316. fido_filter::Action::BLOCK) {
  317. FIDO_LOG(DEBUG) << "Filtered request to device " << authenticator_name
  318. << " for credential ID " << base::HexEncode(cred.id);
  319. return;
  320. }
  321. }
  322. CtapGetAssertionRequest request =
  323. SpecializeRequestForAuthenticator(request_, *authenticator);
  324. PINUVDisposition uv_disposition =
  325. authenticator->PINUVDispositionForGetAssertion(request, observer());
  326. switch (uv_disposition) {
  327. case PINUVDisposition::kNoUV:
  328. case PINUVDisposition::kNoTokenInternalUV:
  329. case PINUVDisposition::kNoTokenInternalUVPINFallback:
  330. // Proceed without a token.
  331. break;
  332. case PINUVDisposition::kGetToken:
  333. ObtainPINUVAuthToken(
  334. authenticator, GetPinTokenPermissionsFor(*authenticator, request),
  335. active_authenticators().size() == 1 && allow_skipping_pin_touch_,
  336. /*internal_uv_locked=*/false);
  337. return;
  338. case PINUVDisposition::kUnsatisfiable:
  339. FIDO_LOG(DEBUG) << authenticator->GetDisplayName()
  340. << " cannot satisfy assertion request. Requesting "
  341. "touch in order to handle error case.";
  342. authenticator->GetTouch(base::BindOnce(
  343. &GetAssertionRequestHandler::TerminateUnsatisfiableRequestPostTouch,
  344. weak_factory_.GetWeakPtr(), authenticator));
  345. return;
  346. }
  347. if (preselected_credential_) {
  348. DCHECK(request.allow_list.empty());
  349. request.allow_list = {device::PublicKeyCredentialDescriptor(
  350. CredentialType::kPublicKey, *preselected_credential_,
  351. {FidoTransportProtocol::kInternal})};
  352. }
  353. ReportGetAssertionRequestTransport(authenticator);
  354. CtapGetAssertionRequest request_copy(request);
  355. authenticator->GetAssertion(
  356. std::move(request_copy), options_,
  357. base::BindOnce(&GetAssertionRequestHandler::HandleResponse,
  358. weak_factory_.GetWeakPtr(), authenticator,
  359. std::move(request), base::ElapsedTimer()));
  360. }
  361. void GetAssertionRequestHandler::AuthenticatorRemoved(
  362. FidoDiscoveryBase* discovery,
  363. FidoAuthenticator* authenticator) {
  364. DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
  365. auth_token_requester_map_.erase(authenticator);
  366. FidoRequestHandlerBase::AuthenticatorRemoved(discovery, authenticator);
  367. if (authenticator == selected_authenticator_for_pin_uv_auth_token_) {
  368. selected_authenticator_for_pin_uv_auth_token_ = nullptr;
  369. // Authenticator could have been removed during PIN entry or PIN fallback
  370. // after failed internal UV. Bail and show an error.
  371. if (state_ != State::kFinished) {
  372. state_ = State::kFinished;
  373. std::move(completion_callback_)
  374. .Run(GetAssertionStatus::kAuthenticatorRemovedDuringPINEntry,
  375. absl::nullopt, nullptr);
  376. }
  377. }
  378. }
  379. void GetAssertionRequestHandler::GetPlatformCredentialStatus(
  380. FidoAuthenticator* platform_authenticator) {
  381. DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
  382. platform_authenticator->GetCredentialInformationForRequest(
  383. request_, base::BindOnce(
  384. &GetAssertionRequestHandler::OnHavePlatformCredentialStatus,
  385. weak_factory_.GetWeakPtr()));
  386. }
  387. bool GetAssertionRequestHandler::AuthenticatorSelectedForPINUVAuthToken(
  388. FidoAuthenticator* authenticator) {
  389. if (state_ != State::kWaitingForTouch) {
  390. // Some other authenticator was selected in the meantime.
  391. FIDO_LOG(DEBUG) << "Rejecting select request from AuthTokenRequester "
  392. "because another authenticator was already selected.";
  393. return false;
  394. }
  395. state_ = State::kWaitingForToken;
  396. selected_authenticator_for_pin_uv_auth_token_ = authenticator;
  397. base::EraseIf(auth_token_requester_map_, [authenticator](auto& entry) {
  398. return entry.first != authenticator;
  399. });
  400. CancelActiveAuthenticators(authenticator->GetId());
  401. return true;
  402. }
  403. void GetAssertionRequestHandler::CollectPIN(pin::PINEntryReason reason,
  404. pin::PINEntryError error,
  405. uint32_t min_pin_length,
  406. int attempts,
  407. ProvidePINCallback provide_pin_cb) {
  408. DCHECK_EQ(state_, State::kWaitingForToken);
  409. observer()->CollectPIN({.reason = reason,
  410. .error = error,
  411. .min_pin_length = min_pin_length,
  412. .attempts = attempts},
  413. std::move(provide_pin_cb));
  414. }
  415. void GetAssertionRequestHandler::PromptForInternalUVRetry(int attempts) {
  416. DCHECK(state_ == State::kWaitingForTouch ||
  417. state_ == State::kWaitingForToken);
  418. observer()->OnRetryUserVerification(attempts);
  419. }
  420. void GetAssertionRequestHandler::HavePINUVAuthTokenResultForAuthenticator(
  421. FidoAuthenticator* authenticator,
  422. AuthTokenRequester::Result result,
  423. absl::optional<pin::TokenResponse> token_response) {
  424. absl::optional<GetAssertionStatus> error;
  425. switch (result) {
  426. case AuthTokenRequester::Result::kPreTouchUnsatisfiableRequest:
  427. case AuthTokenRequester::Result::kPreTouchAuthenticatorResponseInvalid:
  428. FIDO_LOG(ERROR) << "Ignoring AuthTokenRequester::Result="
  429. << static_cast<int>(result) << " from "
  430. << authenticator->GetId();
  431. return;
  432. case AuthTokenRequester::Result::kPostTouchAuthenticatorInternalUVLock:
  433. error = GetAssertionStatus::kAuthenticatorMissingUserVerification;
  434. break;
  435. case AuthTokenRequester::Result::kPostTouchAuthenticatorResponseInvalid:
  436. error = GetAssertionStatus::kAuthenticatorResponseInvalid;
  437. break;
  438. case AuthTokenRequester::Result::kPostTouchAuthenticatorOperationDenied:
  439. error = GetAssertionStatus::kUserConsentDenied;
  440. break;
  441. case AuthTokenRequester::Result::kPostTouchAuthenticatorPINSoftLock:
  442. error = GetAssertionStatus::kSoftPINBlock;
  443. break;
  444. case AuthTokenRequester::Result::kPostTouchAuthenticatorPINHardLock:
  445. error = GetAssertionStatus::kHardPINBlock;
  446. break;
  447. case AuthTokenRequester::Result::kSuccess:
  448. break;
  449. }
  450. // Pre touch events should be handled above.
  451. DCHECK_EQ(state_, State::kWaitingForToken);
  452. DCHECK_EQ(selected_authenticator_for_pin_uv_auth_token_, authenticator);
  453. if (error) {
  454. state_ = State::kFinished;
  455. std::move(completion_callback_).Run(*error, absl::nullopt, authenticator);
  456. return;
  457. }
  458. DCHECK_EQ(result, AuthTokenRequester::Result::kSuccess);
  459. auto request = std::make_unique<CtapGetAssertionRequest>(request_);
  460. SpecializeRequestForAuthenticator(*request, *authenticator);
  461. DispatchRequestWithToken(std::move(*token_response));
  462. }
  463. void GetAssertionRequestHandler::ObtainPINUVAuthToken(
  464. FidoAuthenticator* authenticator,
  465. std::set<pin::Permissions> permissions,
  466. bool skip_pin_touch,
  467. bool internal_uv_locked) {
  468. AuthTokenRequester::Options options;
  469. options.token_permissions = std::move(permissions);
  470. options.rp_id = request_.rp_id;
  471. options.skip_pin_touch = skip_pin_touch;
  472. options.internal_uv_locked = internal_uv_locked;
  473. auth_token_requester_map_.insert(
  474. {authenticator, std::make_unique<AuthTokenRequester>(
  475. this, authenticator, std::move(options))});
  476. auth_token_requester_map_.at(authenticator)->ObtainPINUVAuthToken();
  477. }
  478. void GetAssertionRequestHandler::HandleResponse(
  479. FidoAuthenticator* authenticator,
  480. CtapGetAssertionRequest request,
  481. base::ElapsedTimer request_timer,
  482. CtapDeviceResponseCode status,
  483. absl::optional<AuthenticatorGetAssertionResponse> response) {
  484. DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
  485. if (state_ != State::kWaitingForTouch &&
  486. state_ != State::kWaitingForResponseWithToken) {
  487. FIDO_LOG(DEBUG) << "Ignoring response from "
  488. << authenticator->GetDisplayName()
  489. << " because no longer waiting for touch";
  490. return;
  491. }
  492. if (selected_authenticator_for_pin_uv_auth_token_ &&
  493. authenticator != selected_authenticator_for_pin_uv_auth_token_) {
  494. FIDO_LOG(DEBUG) << "Ignoring response from "
  495. << authenticator->GetDisplayName()
  496. << " because another authenticator was selected";
  497. return;
  498. }
  499. #if BUILDFLAG(IS_WIN)
  500. if (authenticator->GetType() == FidoAuthenticator::Type::kWinNative) {
  501. state_ = State::kFinished;
  502. CancelActiveAuthenticators(authenticator->GetId());
  503. if (status != CtapDeviceResponseCode::kSuccess) {
  504. std::move(completion_callback_)
  505. .Run(WinCtapDeviceResponseCodeToGetAssertionStatus(status),
  506. absl::nullopt, authenticator);
  507. return;
  508. }
  509. if (!ResponseValid(/*is_first_response=*/true, *authenticator, request,
  510. options_, *response)) {
  511. FIDO_LOG(ERROR) << "Failing assertion request due to bad response from "
  512. << authenticator->GetDisplayName();
  513. std::move(completion_callback_)
  514. .Run(GetAssertionStatus::kWinNotAllowedError, absl::nullopt,
  515. authenticator);
  516. return;
  517. }
  518. DCHECK(responses_.empty());
  519. responses_.emplace_back(std::move(*response));
  520. std::move(completion_callback_)
  521. .Run(WinCtapDeviceResponseCodeToGetAssertionStatus(status),
  522. std::move(responses_), authenticator);
  523. return;
  524. }
  525. #endif
  526. // If we requested UV from an authentiator without uvToken support, UV failed,
  527. // and the authenticator supports PIN, fall back to that.
  528. if (request.user_verification != UserVerificationRequirement::kDiscouraged &&
  529. !request.pin_auth &&
  530. (status == CtapDeviceResponseCode::kCtap2ErrPinAuthInvalid ||
  531. status == CtapDeviceResponseCode::kCtap2ErrPinRequired ||
  532. status == CtapDeviceResponseCode::kCtap2ErrOperationDenied) &&
  533. authenticator->PINUVDispositionForGetAssertion(request, observer()) ==
  534. PINUVDisposition::kNoTokenInternalUVPINFallback) {
  535. // Authenticators without uvToken support will return this error immediately
  536. // without user interaction when internal UV is locked.
  537. const base::TimeDelta response_time = request_timer.Elapsed();
  538. if (response_time < kMinExpectedAuthenticatorResponseTime) {
  539. FIDO_LOG(DEBUG) << "Authenticator is probably locked, response_time="
  540. << response_time;
  541. ObtainPINUVAuthToken(
  542. authenticator, GetPinTokenPermissionsFor(*authenticator, request),
  543. /*skip_pin_touch=*/false, /*internal_uv_locked=*/true);
  544. return;
  545. }
  546. ObtainPINUVAuthToken(authenticator,
  547. GetPinTokenPermissionsFor(*authenticator, request),
  548. /*skip_pin_touch=*/true, /*internal_uv_locked=*/true);
  549. return;
  550. }
  551. const absl::optional<GetAssertionStatus> maybe_result =
  552. ConvertDeviceResponseCode(status);
  553. if (!maybe_result) {
  554. if (state_ == State::kWaitingForResponseWithToken) {
  555. std::move(completion_callback_)
  556. .Run(GetAssertionStatus::kAuthenticatorResponseInvalid, absl::nullopt,
  557. authenticator);
  558. } else {
  559. FIDO_LOG(ERROR) << "Ignoring status " << static_cast<int>(status)
  560. << " from " << authenticator->GetDisplayName();
  561. }
  562. return;
  563. }
  564. state_ = State::kFinished;
  565. CancelActiveAuthenticators(authenticator->GetId());
  566. if (status != CtapDeviceResponseCode::kSuccess) {
  567. FIDO_LOG(ERROR) << "Failing assertion request due to status "
  568. << static_cast<int>(status) << " from "
  569. << authenticator->GetDisplayName();
  570. std::move(completion_callback_)
  571. .Run(*maybe_result, absl::nullopt, authenticator);
  572. return;
  573. }
  574. if (!response || !ResponseValid(/*is_first_response=*/true, *authenticator,
  575. request, options_, *response)) {
  576. FIDO_LOG(ERROR) << "Failing assertion request due to bad response from "
  577. << authenticator->GetDisplayName();
  578. std::move(completion_callback_)
  579. .Run(GetAssertionStatus::kAuthenticatorResponseInvalid, absl::nullopt,
  580. authenticator);
  581. return;
  582. }
  583. const size_t num_responses = response->num_credentials.value_or(1);
  584. if (num_responses == 0 ||
  585. (num_responses > 1 && !request.allow_list.empty())) {
  586. std::move(completion_callback_)
  587. .Run(GetAssertionStatus::kAuthenticatorResponseInvalid, absl::nullopt,
  588. authenticator);
  589. return;
  590. }
  591. if (preselected_credential_) {
  592. // A discoverable platform credential was preselected by the user prior to
  593. // making the assertion request. Instruct the UI not to show another account
  594. // selection dialog by setting the `userSelected` flag.
  595. DCHECK_EQ(num_responses, 1u);
  596. DCHECK(response->credential &&
  597. response->credential->id == preselected_credential_);
  598. response->user_selected = true;
  599. }
  600. DCHECK(responses_.empty());
  601. responses_.emplace_back(std::move(*response));
  602. if (num_responses > 1) {
  603. // Multiple responses. Need to read them all.
  604. state_ = State::kReadingMultipleResponses;
  605. remaining_responses_ = num_responses - 1;
  606. authenticator->GetNextAssertion(base::BindOnce(
  607. &GetAssertionRequestHandler::HandleNextResponse,
  608. weak_factory_.GetWeakPtr(), authenticator, std::move(request)));
  609. return;
  610. }
  611. ReportGetAssertionResponseTransport(authenticator);
  612. OnGetAssertionSuccess(authenticator, std::move(request));
  613. }
  614. void GetAssertionRequestHandler::HandleNextResponse(
  615. FidoAuthenticator* authenticator,
  616. CtapGetAssertionRequest request,
  617. CtapDeviceResponseCode status,
  618. absl::optional<AuthenticatorGetAssertionResponse> response) {
  619. DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
  620. DCHECK_EQ(State::kReadingMultipleResponses, state_);
  621. DCHECK_LT(0u, remaining_responses_);
  622. state_ = State::kFinished;
  623. if (status != CtapDeviceResponseCode::kSuccess) {
  624. FIDO_LOG(ERROR) << "Failing assertion request due to status "
  625. << static_cast<int>(status) << " from "
  626. << authenticator->GetDisplayName();
  627. std::move(completion_callback_)
  628. .Run(GetAssertionStatus::kAuthenticatorResponseInvalid, absl::nullopt,
  629. authenticator);
  630. return;
  631. }
  632. if (!ResponseValid(/*is_first_response=*/false, *authenticator, request,
  633. options_, *response)) {
  634. FIDO_LOG(ERROR) << "Failing assertion request due to bad response from "
  635. << authenticator->GetDisplayName();
  636. std::move(completion_callback_)
  637. .Run(GetAssertionStatus::kAuthenticatorResponseInvalid, absl::nullopt,
  638. authenticator);
  639. return;
  640. }
  641. DCHECK(!responses_.empty());
  642. responses_.emplace_back(std::move(*response));
  643. remaining_responses_--;
  644. if (remaining_responses_ > 0) {
  645. state_ = State::kReadingMultipleResponses;
  646. authenticator->GetNextAssertion(base::BindOnce(
  647. &GetAssertionRequestHandler::HandleNextResponse,
  648. weak_factory_.GetWeakPtr(), authenticator, std::move(request)));
  649. return;
  650. }
  651. ReportGetAssertionResponseTransport(authenticator);
  652. OnGetAssertionSuccess(authenticator, std::move(request));
  653. }
  654. void GetAssertionRequestHandler::TerminateUnsatisfiableRequestPostTouch(
  655. FidoAuthenticator* authenticator) {
  656. // User touched an authenticator that cannot handle this request or internal
  657. // user verification has failed but the authenticator does not support PIN.
  658. // The latter should not happen, show an error to the user as well.
  659. state_ = State::kFinished;
  660. CancelActiveAuthenticators(authenticator->GetId());
  661. std::move(completion_callback_)
  662. .Run(GetAssertionStatus::kAuthenticatorMissingUserVerification,
  663. absl::nullopt, nullptr);
  664. }
  665. void GetAssertionRequestHandler::DispatchRequestWithToken(
  666. pin::TokenResponse token) {
  667. DCHECK(selected_authenticator_for_pin_uv_auth_token_);
  668. observer()->FinishCollectToken();
  669. pin_token_ = std::move(token);
  670. state_ = State::kWaitingForResponseWithToken;
  671. CtapGetAssertionRequest request = SpecializeRequestForAuthenticator(
  672. request_, *selected_authenticator_for_pin_uv_auth_token_);
  673. std::tie(request.pin_protocol, request.pin_auth) =
  674. pin_token_->PinAuth(request.client_data_hash);
  675. ReportGetAssertionRequestTransport(
  676. selected_authenticator_for_pin_uv_auth_token_);
  677. auto request_copy(request);
  678. selected_authenticator_for_pin_uv_auth_token_->GetAssertion(
  679. std::move(request_copy), options_,
  680. base::BindOnce(&GetAssertionRequestHandler::HandleResponse,
  681. weak_factory_.GetWeakPtr(),
  682. selected_authenticator_for_pin_uv_auth_token_,
  683. std::move(request), base::ElapsedTimer()));
  684. }
  685. void GetAssertionRequestHandler::OnGetAssertionSuccess(
  686. FidoAuthenticator* authenticator,
  687. CtapGetAssertionRequest request) {
  688. if (request.large_blob_read || request.large_blob_write) {
  689. DCHECK(authenticator->Options()->supports_large_blobs);
  690. std::vector<LargeBlobKey> keys;
  691. for (const auto& response : responses_) {
  692. if (response.large_blob_key) {
  693. keys.emplace_back(*response.large_blob_key);
  694. }
  695. }
  696. if (!keys.empty()) {
  697. if (request.large_blob_read) {
  698. authenticator->ReadLargeBlob(
  699. keys, pin_token_,
  700. base::BindOnce(&GetAssertionRequestHandler::OnReadLargeBlobs,
  701. weak_factory_.GetWeakPtr(), authenticator));
  702. return;
  703. }
  704. DCHECK(request.large_blob_write);
  705. DCHECK_EQ(1u, keys.size());
  706. authenticator->WriteLargeBlob(
  707. *request.large_blob_write, keys.at(0), pin_token_,
  708. base::BindOnce(&GetAssertionRequestHandler::OnWriteLargeBlob,
  709. weak_factory_.GetWeakPtr(), authenticator));
  710. return;
  711. }
  712. }
  713. std::move(completion_callback_)
  714. .Run(GetAssertionStatus::kSuccess, std::move(responses_), authenticator);
  715. }
  716. void GetAssertionRequestHandler::OnReadLargeBlobs(
  717. FidoAuthenticator* authenticator,
  718. CtapDeviceResponseCode status,
  719. absl::optional<std::vector<std::pair<LargeBlobKey, LargeBlob>>> blobs) {
  720. if (status == CtapDeviceResponseCode::kSuccess) {
  721. for (auto& response : responses_) {
  722. const auto blob =
  723. base::ranges::find_if(*blobs, [&response](const auto& pair) {
  724. return pair.first == response.large_blob_key;
  725. });
  726. if (blob != blobs->end()) {
  727. response.large_blob = std::move(blob->second);
  728. }
  729. }
  730. } else {
  731. FIDO_LOG(ERROR) << "Reading large blob failed with code "
  732. << static_cast<int>(status);
  733. }
  734. std::move(completion_callback_)
  735. .Run(GetAssertionStatus::kSuccess, std::move(responses_), authenticator);
  736. }
  737. void GetAssertionRequestHandler::OnWriteLargeBlob(
  738. FidoAuthenticator* authenticator,
  739. CtapDeviceResponseCode status) {
  740. if (status != CtapDeviceResponseCode::kSuccess) {
  741. FIDO_LOG(ERROR) << "Writing large blob failed with code "
  742. << static_cast<int>(status);
  743. }
  744. responses_.at(0).large_blob_written =
  745. (status == CtapDeviceResponseCode::kSuccess);
  746. std::move(completion_callback_)
  747. .Run(GetAssertionStatus::kSuccess, std::move(responses_), authenticator);
  748. }
  749. } // namespace device