make_credential_request_handler.cc 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051
  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/make_credential_request_handler.h"
  5. #include <set>
  6. #include <utility>
  7. #include "base/barrier_closure.h"
  8. #include "base/bind.h"
  9. #include "base/containers/contains.h"
  10. #include "base/containers/cxx20_erase.h"
  11. #include "base/metrics/histogram_functions.h"
  12. #include "base/stl_util.h"
  13. #include "base/strings/string_number_conversions.h"
  14. #include "build/build_config.h"
  15. #include "build/chromeos_buildflags.h"
  16. #include "components/cbor/diagnostic_writer.h"
  17. #include "components/device_event_log/device_event_log.h"
  18. #include "device/fido/fido_authenticator.h"
  19. #include "device/fido/fido_discovery_factory.h"
  20. #include "device/fido/fido_parsing_utils.h"
  21. #include "device/fido/fido_transport_protocol.h"
  22. #include "device/fido/filter.h"
  23. #include "device/fido/make_credential_task.h"
  24. #if BUILDFLAG(IS_WIN)
  25. #include "device/fido/win/authenticator.h"
  26. #include "device/fido/win/type_conversions.h"
  27. #include "third_party/microsoft_webauthn/webauthn.h"
  28. #endif
  29. #if BUILDFLAG(IS_CHROMEOS)
  30. #include "device/fido/cros/authenticator.h"
  31. #endif
  32. namespace device {
  33. using PINUVDisposition = FidoAuthenticator::PINUVDisposition;
  34. using BioEnrollmentAvailability =
  35. AuthenticatorSupportedOptions::BioEnrollmentAvailability;
  36. namespace {
  37. // Permissions requested for PinUvAuthToken. GetAssertion is needed for silent
  38. // probing of credentials.
  39. const std::set<pin::Permissions> GetMakeCredentialRequestPermissions(
  40. FidoAuthenticator* authenticator) {
  41. std::set<pin::Permissions> permissions = {pin::Permissions::kMakeCredential,
  42. pin::Permissions::kGetAssertion};
  43. if (authenticator->Options() &&
  44. authenticator->Options()->bio_enrollment_availability ==
  45. BioEnrollmentAvailability::kSupportedButUnprovisioned) {
  46. permissions.insert(pin::Permissions::kBioEnrollment);
  47. }
  48. return permissions;
  49. }
  50. absl::optional<MakeCredentialStatus> ConvertDeviceResponseCode(
  51. CtapDeviceResponseCode device_response_code) {
  52. switch (device_response_code) {
  53. case CtapDeviceResponseCode::kSuccess:
  54. return MakeCredentialStatus::kSuccess;
  55. // Only returned after the user interacted with the authenticator.
  56. case CtapDeviceResponseCode::kCtap2ErrCredentialExcluded:
  57. return MakeCredentialStatus::kUserConsentButCredentialExcluded;
  58. // The user explicitly denied the operation. Touch ID returns this error
  59. // when the user cancels the macOS prompt. External authenticators may
  60. // return it e.g. after the user fails fingerprint verification.
  61. case CtapDeviceResponseCode::kCtap2ErrOperationDenied:
  62. return MakeCredentialStatus::kUserConsentDenied;
  63. // External authenticators may return this error if internal user
  64. // verification fails for a make credential request or if the pin token is
  65. // not valid.
  66. case CtapDeviceResponseCode::kCtap2ErrPinAuthInvalid:
  67. return MakeCredentialStatus::kUserConsentDenied;
  68. case CtapDeviceResponseCode::kCtap2ErrKeyStoreFull:
  69. return MakeCredentialStatus::kStorageFull;
  70. // For all other errors, the authenticator will be dropped, and other
  71. // authenticators may continue.
  72. default:
  73. return absl::nullopt;
  74. }
  75. }
  76. // IsCandidateAuthenticatorPreTouch returns true if the given authenticator
  77. // should even blink for a request.
  78. bool IsCandidateAuthenticatorPreTouch(
  79. FidoAuthenticator* authenticator,
  80. AuthenticatorAttachment requested_attachment,
  81. bool allow_platform_authenticator_for_make_credential_request) {
  82. const auto& opt_options = authenticator->Options();
  83. if (!opt_options) {
  84. // This authenticator doesn't know its capabilities yet, so we need
  85. // to assume it can handle the request. This is the case for Windows,
  86. // where we proxy the request to the native API.
  87. return true;
  88. }
  89. if ((requested_attachment == AuthenticatorAttachment::kPlatform &&
  90. !opt_options->is_platform_device) ||
  91. (requested_attachment == AuthenticatorAttachment::kCrossPlatform &&
  92. opt_options->is_platform_device &&
  93. !allow_platform_authenticator_for_make_credential_request)) {
  94. return false;
  95. }
  96. return true;
  97. }
  98. // IsCandidateAuthenticatorPostTouch returns a value other than |kSuccess| if
  99. // the given authenticator cannot handle a request.
  100. MakeCredentialStatus IsCandidateAuthenticatorPostTouch(
  101. const CtapMakeCredentialRequest& request,
  102. FidoAuthenticator* authenticator,
  103. const MakeCredentialOptions& options,
  104. const FidoRequestHandlerBase::Observer* observer) {
  105. if (options.cred_protect_request && options.cred_protect_request->second &&
  106. !authenticator->SupportsCredProtectExtension()) {
  107. return MakeCredentialStatus::kAuthenticatorMissingResidentKeys;
  108. }
  109. const absl::optional<AuthenticatorSupportedOptions>& auth_options =
  110. authenticator->Options();
  111. if (!auth_options) {
  112. // This authenticator doesn't know its capabilities yet, so we need
  113. // to assume it can handle the request. This is the case for Windows,
  114. // where we proxy the request to the native API.
  115. return MakeCredentialStatus::kSuccess;
  116. }
  117. #if BUILDFLAG(IS_CHROMEOS)
  118. // Allow dispatch of UP-only cross-platform requests to the platform
  119. // authenticator to ensure backwards compatibility with the legacy
  120. // DeviceSecondFactorAuthentication enterprise policy.
  121. if (options.authenticator_attachment ==
  122. AuthenticatorAttachment::kCrossPlatform &&
  123. auth_options->is_platform_device) {
  124. if (options.resident_key == ResidentKeyRequirement::kRequired) {
  125. return MakeCredentialStatus::kAuthenticatorMissingResidentKeys;
  126. }
  127. if (options.user_verification == UserVerificationRequirement::kRequired) {
  128. return MakeCredentialStatus::kAuthenticatorMissingUserVerification;
  129. }
  130. return MakeCredentialStatus::kSuccess;
  131. }
  132. #endif
  133. if (options.resident_key == ResidentKeyRequirement::kRequired &&
  134. !auth_options->supports_resident_key) {
  135. return MakeCredentialStatus::kAuthenticatorMissingResidentKeys;
  136. }
  137. if (authenticator->PINUVDispositionForMakeCredential(request, observer) ==
  138. PINUVDisposition::kUnsatisfiable) {
  139. return MakeCredentialStatus::kAuthenticatorMissingUserVerification;
  140. }
  141. // The largeBlobs extension only works for resident credentials on CTAP 2.1
  142. // authenticators.
  143. if (options.large_blob_support == LargeBlobSupport::kRequired &&
  144. (!auth_options->supports_large_blobs || !request.resident_key_required)) {
  145. return MakeCredentialStatus::kAuthenticatorMissingLargeBlob;
  146. }
  147. absl::optional<base::span<const int32_t>> supported_algorithms(
  148. authenticator->GetAlgorithms());
  149. if (supported_algorithms) {
  150. // Substitution of defaults should have happened by this point.
  151. DCHECK(!request.public_key_credential_params.public_key_credential_params()
  152. .empty());
  153. bool at_least_one_common_algorithm = false;
  154. for (const auto& algo :
  155. request.public_key_credential_params.public_key_credential_params()) {
  156. if (algo.type != CredentialType::kPublicKey) {
  157. continue;
  158. }
  159. if (base::Contains(*supported_algorithms, algo.algorithm)) {
  160. at_least_one_common_algorithm = true;
  161. break;
  162. }
  163. }
  164. if (!at_least_one_common_algorithm) {
  165. return MakeCredentialStatus::kNoCommonAlgorithms;
  166. }
  167. }
  168. return MakeCredentialStatus::kSuccess;
  169. }
  170. base::flat_set<FidoTransportProtocol> GetTransportsAllowedByRP(
  171. AuthenticatorAttachment authenticator_attachment) {
  172. switch (authenticator_attachment) {
  173. case AuthenticatorAttachment::kPlatform:
  174. return {FidoTransportProtocol::kInternal};
  175. case AuthenticatorAttachment::kCrossPlatform:
  176. return {
  177. FidoTransportProtocol::kUsbHumanInterfaceDevice,
  178. FidoTransportProtocol::kBluetoothLowEnergy,
  179. FidoTransportProtocol::kNearFieldCommunication,
  180. FidoTransportProtocol::kHybrid,
  181. FidoTransportProtocol::kAndroidAccessory,
  182. };
  183. case AuthenticatorAttachment::kAny:
  184. return {
  185. FidoTransportProtocol::kInternal,
  186. FidoTransportProtocol::kNearFieldCommunication,
  187. FidoTransportProtocol::kUsbHumanInterfaceDevice,
  188. FidoTransportProtocol::kBluetoothLowEnergy,
  189. FidoTransportProtocol::kHybrid,
  190. FidoTransportProtocol::kAndroidAccessory,
  191. };
  192. }
  193. NOTREACHED();
  194. return base::flat_set<FidoTransportProtocol>();
  195. }
  196. void ReportMakeCredentialRequestTransport(FidoAuthenticator* authenticator) {
  197. if (authenticator->AuthenticatorTransport()) {
  198. base::UmaHistogramEnumeration(
  199. "WebAuthentication.MakeCredentialRequestTransport",
  200. *authenticator->AuthenticatorTransport());
  201. }
  202. }
  203. // CredProtectForAuthenticator translates a |CredProtectRequest| to a
  204. // |CredProtect| value given the capabilities of a specific authenticator.
  205. CredProtect CredProtectForAuthenticator(
  206. CredProtectRequest request,
  207. const FidoAuthenticator& authenticator) {
  208. switch (request) {
  209. case CredProtectRequest::kUVOptional:
  210. return CredProtect::kUVOptional;
  211. case CredProtectRequest::kUVOrCredIDRequired:
  212. return CredProtect::kUVOrCredIDRequired;
  213. case CredProtectRequest::kUVRequired:
  214. return CredProtect::kUVRequired;
  215. case CredProtectRequest::kUVOrCredIDRequiredOrBetter:
  216. if (authenticator.Options() &&
  217. authenticator.Options()->default_cred_protect ==
  218. CredProtect::kUVRequired) {
  219. return CredProtect::kUVRequired;
  220. }
  221. return CredProtect::kUVOrCredIDRequired;
  222. }
  223. }
  224. // ValidateResponseExtensions returns true iff |extensions| is valid as a
  225. // response to |request| from an authenticator that reports that it supports
  226. // |options|.
  227. bool ValidateResponseExtensions(const CtapMakeCredentialRequest& request,
  228. const MakeCredentialOptions& options,
  229. const FidoAuthenticator& authenticator,
  230. const cbor::Value& extensions) {
  231. if (!extensions.is_map()) {
  232. return false;
  233. }
  234. for (const auto& it : extensions.GetMap()) {
  235. if (!it.first.is_string()) {
  236. return false;
  237. }
  238. const std::string& ext_name = it.first.GetString();
  239. if (ext_name == kExtensionCredProtect) {
  240. if (!authenticator.SupportsCredProtectExtension() ||
  241. !it.second.is_integer()) {
  242. return false;
  243. }
  244. // The authenticator can return any valid credProtect value that is
  245. // equal to, or greater than, what was requested, including when
  246. // nothing was requested.
  247. const int64_t requested_level =
  248. options.cred_protect_request
  249. ? static_cast<int64_t>(CredProtectForAuthenticator(
  250. options.cred_protect_request->first, authenticator))
  251. : 1;
  252. const int64_t returned_level = it.second.GetInteger();
  253. if (returned_level < requested_level ||
  254. returned_level >
  255. base::strict_cast<int64_t>(CredProtect::kUVRequired)) {
  256. FIDO_LOG(ERROR) << "Returned credProtect level (" << returned_level
  257. << ") is invalid or less than the requested level ("
  258. << requested_level << ")";
  259. return false;
  260. }
  261. } else if (ext_name == kExtensionHmacSecret) {
  262. if (!request.hmac_secret || !it.second.is_bool()) {
  263. return false;
  264. }
  265. } else if (ext_name == kExtensionCredBlob) {
  266. if (!request.cred_blob || !it.second.is_bool()) {
  267. return false;
  268. }
  269. } else if (ext_name == kExtensionMinPINLength) {
  270. if (!request.min_pin_length_requested || !it.second.is_unsigned()) {
  271. return false;
  272. }
  273. } else {
  274. // Authenticators may not return unknown extensions.
  275. return false;
  276. }
  277. }
  278. return true;
  279. }
  280. // ResponseValid returns whether |response| is permissible for the given
  281. // |authenticator| and |request|.
  282. bool ResponseValid(const FidoAuthenticator& authenticator,
  283. const CtapMakeCredentialRequest& request,
  284. const AuthenticatorMakeCredentialResponse& response,
  285. const MakeCredentialOptions& options) {
  286. if (response.GetRpIdHash() !=
  287. fido_parsing_utils::CreateSHA256Hash(request.rp.id)) {
  288. FIDO_LOG(ERROR) << "Invalid RP ID hash";
  289. return false;
  290. }
  291. const absl::optional<cbor::Value>& extensions =
  292. response.attestation_object().authenticator_data().extensions();
  293. if (extensions && !ValidateResponseExtensions(request, options, authenticator,
  294. *extensions)) {
  295. FIDO_LOG(ERROR) << "Invalid extensions block: "
  296. << cbor::DiagnosticWriter::Write(*extensions);
  297. return false;
  298. }
  299. if (response.enterprise_attestation_returned &&
  300. (request.attestation_preference !=
  301. AttestationConveyancePreference::
  302. kEnterpriseIfRPListedOnAuthenticator &&
  303. request.attestation_preference !=
  304. AttestationConveyancePreference::kEnterpriseApprovedByBrowser)) {
  305. FIDO_LOG(ERROR) << "Enterprise attestation returned but not requested.";
  306. return false;
  307. }
  308. if (request.large_blob_key && !response.large_blob_key()) {
  309. FIDO_LOG(ERROR) << "Large blob key requested but not returned";
  310. return false;
  311. }
  312. return true;
  313. }
  314. } // namespace
  315. MakeCredentialRequestHandler::MakeCredentialRequestHandler(
  316. FidoDiscoveryFactory* fido_discovery_factory,
  317. const base::flat_set<FidoTransportProtocol>& supported_transports,
  318. CtapMakeCredentialRequest request,
  319. const MakeCredentialOptions& options,
  320. CompletionCallback completion_callback)
  321. : completion_callback_(std::move(completion_callback)),
  322. request_(std::move(request)),
  323. options_(options) {
  324. // These parts of the request should be filled in by
  325. // |SpecializeRequestForAuthenticator|.
  326. DCHECK_EQ(request_.authenticator_attachment, AuthenticatorAttachment::kAny);
  327. DCHECK(!request_.resident_key_required);
  328. DCHECK(!request_.cred_protect);
  329. DCHECK(!request_.cred_protect_enforce);
  330. transport_availability_info().request_type = FidoRequestType::kMakeCredential;
  331. transport_availability_info().is_off_the_record_context =
  332. options_.is_off_the_record_context;
  333. transport_availability_info().resident_key_requirement =
  334. options_.resident_key;
  335. base::flat_set<FidoTransportProtocol> allowed_transports =
  336. GetTransportsAllowedByRP(options.authenticator_attachment);
  337. #if BUILDFLAG(IS_CHROMEOS)
  338. // Attempt to instantiate the ChromeOS platform authenticator for
  339. // power-button-only requests for compatibility with the legacy
  340. // DeviceSecondFactorAuthentication policy, if that policy is enabled.
  341. if (!options_.make_u2f_api_credential &&
  342. options_.authenticator_attachment ==
  343. AuthenticatorAttachment::kCrossPlatform) {
  344. allow_platform_authenticator_for_cross_platform_request_ = true;
  345. fido_discovery_factory->set_require_legacy_cros_authenticator(true);
  346. allowed_transports.insert(FidoTransportProtocol::kInternal);
  347. }
  348. #endif
  349. InitDiscoveries(
  350. fido_discovery_factory,
  351. base::STLSetIntersection<base::flat_set<FidoTransportProtocol>>(
  352. supported_transports, allowed_transports));
  353. Start();
  354. }
  355. MakeCredentialRequestHandler::~MakeCredentialRequestHandler() = default;
  356. void MakeCredentialRequestHandler::DispatchRequest(
  357. FidoAuthenticator* authenticator) {
  358. DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
  359. if (state_ != State::kWaitingForTouch ||
  360. !IsCandidateAuthenticatorPreTouch(
  361. authenticator, options_.authenticator_attachment,
  362. allow_platform_authenticator_for_cross_platform_request_)) {
  363. return;
  364. }
  365. const std::string authenticator_name = authenticator->GetDisplayName();
  366. switch (fido_filter::Evaluate(
  367. fido_filter::Operation::MAKE_CREDENTIAL, request_.rp.id,
  368. authenticator_name,
  369. std::pair<fido_filter::IDType, base::span<const uint8_t>>(
  370. fido_filter::IDType::USER_ID, request_.user.id))) {
  371. case fido_filter::Action::ALLOW:
  372. break;
  373. case fido_filter::Action::NO_ATTESTATION:
  374. suppress_attestation_ = true;
  375. break;
  376. case fido_filter::Action::BLOCK:
  377. FIDO_LOG(DEBUG) << "Filtered request to device " << authenticator_name;
  378. return;
  379. }
  380. for (const auto& cred : request_.exclude_list) {
  381. if (fido_filter::Evaluate(
  382. fido_filter::Operation::MAKE_CREDENTIAL, request_.rp.id,
  383. authenticator_name,
  384. std::pair<fido_filter::IDType, base::span<const uint8_t>>(
  385. fido_filter::IDType::CREDENTIAL_ID, cred.id)) ==
  386. fido_filter::Action::BLOCK) {
  387. FIDO_LOG(DEBUG) << "Filtered request to device " << authenticator_name
  388. << " for credential ID " << base::HexEncode(cred.id);
  389. return;
  390. }
  391. }
  392. std::unique_ptr<CtapMakeCredentialRequest> request(
  393. new CtapMakeCredentialRequest(request_));
  394. SpecializeRequestForAuthenticator(request.get(), authenticator);
  395. const MakeCredentialStatus post_touch_status =
  396. IsCandidateAuthenticatorPostTouch(*request.get(), authenticator, options_,
  397. observer());
  398. if (post_touch_status != MakeCredentialStatus::kSuccess) {
  399. #if BUILDFLAG(IS_WIN)
  400. // If the Windows API cannot handle a request, just reject the request
  401. // outright. There are no other authenticators to attempt, so calling
  402. // GetTouch() would not make sense.
  403. if (authenticator->GetType() == FidoAuthenticator::Type::kWinNative) {
  404. HandleInapplicableAuthenticator(authenticator, post_touch_status);
  405. return;
  406. }
  407. #endif // BUILDFLAG(IS_WIN)
  408. if (authenticator->Options() &&
  409. authenticator->Options()->is_platform_device) {
  410. HandleInapplicableAuthenticator(authenticator, post_touch_status);
  411. return;
  412. }
  413. // This authenticator does not meet requirements, but make it flash anyway
  414. // so the user understands that it's functional. A descriptive error message
  415. // will be shown if the user selects it.
  416. authenticator->GetTouch(base::BindOnce(
  417. &MakeCredentialRequestHandler::HandleInapplicableAuthenticator,
  418. weak_factory_.GetWeakPtr(), authenticator, post_touch_status));
  419. return;
  420. }
  421. if (request->app_id_exclude && !request->exclude_list.empty()) {
  422. auto request_copy = *request;
  423. authenticator->ExcludeAppIdCredentialsBeforeMakeCredential(
  424. std::move(request_copy), options_,
  425. base::BindOnce(
  426. &MakeCredentialRequestHandler::DispatchRequestAfterAppIdExclude,
  427. weak_factory_.GetWeakPtr(), std::move(request), authenticator));
  428. } else {
  429. DispatchRequestAfterAppIdExclude(std::move(request), authenticator,
  430. CtapDeviceResponseCode::kSuccess,
  431. absl::nullopt);
  432. }
  433. }
  434. void MakeCredentialRequestHandler::DispatchRequestAfterAppIdExclude(
  435. std::unique_ptr<CtapMakeCredentialRequest> request,
  436. FidoAuthenticator* authenticator,
  437. CtapDeviceResponseCode status,
  438. absl::optional<bool> unused) {
  439. if (state_ != State::kWaitingForTouch) {
  440. return;
  441. }
  442. switch (status) {
  443. case CtapDeviceResponseCode::kSuccess:
  444. break;
  445. case CtapDeviceResponseCode::kCtap2ErrCredentialExcluded:
  446. // This authenticator contains an excluded credential. If touched, fail
  447. // the request.
  448. authenticator->GetTouch(base::BindOnce(
  449. &MakeCredentialRequestHandler::HandleExcludedAuthenticator,
  450. weak_factory_.GetWeakPtr(), authenticator));
  451. return;
  452. default:
  453. std::move(completion_callback_)
  454. .Run(MakeCredentialStatus::kAuthenticatorResponseInvalid,
  455. absl::nullopt, authenticator);
  456. return;
  457. }
  458. const bool skip_pin_touch =
  459. active_authenticators().size() == 1 && options_.allow_skipping_pin_touch;
  460. auto uv_disposition = authenticator->PINUVDispositionForMakeCredential(
  461. *request.get(), observer());
  462. switch (uv_disposition) {
  463. case PINUVDisposition::kNoUV:
  464. case PINUVDisposition::kNoTokenInternalUV:
  465. case PINUVDisposition::kNoTokenInternalUVPINFallback:
  466. break;
  467. case PINUVDisposition::kGetToken:
  468. ObtainPINUVAuthToken(authenticator, skip_pin_touch,
  469. /*internal_uv_locked=*/false);
  470. return;
  471. case PINUVDisposition::kUnsatisfiable:
  472. // |IsCandidateAuthenticatorPostTouch| should have handled this case.
  473. NOTREACHED();
  474. return;
  475. }
  476. ReportMakeCredentialRequestTransport(authenticator);
  477. auto request_copy(*request.get()); // can't copy and move in the same stmt.
  478. authenticator->MakeCredential(
  479. std::move(request_copy), options_,
  480. base::BindOnce(&MakeCredentialRequestHandler::HandleResponse,
  481. weak_factory_.GetWeakPtr(), authenticator,
  482. std::move(request), base::ElapsedTimer()));
  483. }
  484. void MakeCredentialRequestHandler::AuthenticatorRemoved(
  485. FidoDiscoveryBase* discovery,
  486. FidoAuthenticator* authenticator) {
  487. DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
  488. auth_token_requester_map_.erase(authenticator);
  489. FidoRequestHandlerBase::AuthenticatorRemoved(discovery, authenticator);
  490. if (authenticator == selected_authenticator_for_pin_uv_auth_token_) {
  491. selected_authenticator_for_pin_uv_auth_token_ = nullptr;
  492. // Authenticator could have been removed during PIN entry, PIN fallback
  493. // after failed internal UV, or bio enrollment. Bail and show an error.
  494. if (state_ != State::kFinished) {
  495. state_ = State::kFinished;
  496. std::move(completion_callback_)
  497. .Run(MakeCredentialStatus::kAuthenticatorRemovedDuringPINEntry,
  498. absl::nullopt, nullptr);
  499. }
  500. }
  501. }
  502. bool MakeCredentialRequestHandler::AuthenticatorSelectedForPINUVAuthToken(
  503. FidoAuthenticator* authenticator) {
  504. if (state_ != State::kWaitingForTouch) {
  505. // Some other authenticator was selected in the meantime.
  506. FIDO_LOG(DEBUG) << "Rejecting select request from AuthTokenRequester "
  507. "because another authenticator was already selected.";
  508. return false;
  509. }
  510. state_ = State::kWaitingForToken;
  511. selected_authenticator_for_pin_uv_auth_token_ = authenticator;
  512. base::EraseIf(auth_token_requester_map_, [authenticator](auto& entry) {
  513. return entry.first != authenticator;
  514. });
  515. CancelActiveAuthenticators(authenticator->GetId());
  516. return true;
  517. }
  518. void MakeCredentialRequestHandler::CollectPIN(
  519. pin::PINEntryReason reason,
  520. pin::PINEntryError error,
  521. uint32_t min_pin_length,
  522. int attempts,
  523. ProvidePINCallback provide_pin_cb) {
  524. DCHECK_EQ(state_, State::kWaitingForToken);
  525. observer()->CollectPIN({.reason = reason,
  526. .error = error,
  527. .min_pin_length = min_pin_length,
  528. .attempts = attempts},
  529. std::move(provide_pin_cb));
  530. }
  531. void MakeCredentialRequestHandler::PromptForInternalUVRetry(int attempts) {
  532. if (state_ != State::kWaitingForTouch && state_ != State::kWaitingForToken) {
  533. // Some other authenticator was touched in the meantime.
  534. return;
  535. }
  536. observer()->OnRetryUserVerification(attempts);
  537. }
  538. void MakeCredentialRequestHandler::HavePINUVAuthTokenResultForAuthenticator(
  539. FidoAuthenticator* authenticator,
  540. AuthTokenRequester::Result result,
  541. absl::optional<pin::TokenResponse> token_response) {
  542. absl::optional<MakeCredentialStatus> error;
  543. switch (result) {
  544. case AuthTokenRequester::Result::kPreTouchUnsatisfiableRequest:
  545. case AuthTokenRequester::Result::kPreTouchAuthenticatorResponseInvalid:
  546. FIDO_LOG(ERROR) << "Ignoring MakeCredentialStatus="
  547. << static_cast<int>(result) << " from "
  548. << authenticator->GetId();
  549. return;
  550. case AuthTokenRequester::Result::kPostTouchAuthenticatorInternalUVLock:
  551. error = MakeCredentialStatus::kAuthenticatorMissingUserVerification;
  552. break;
  553. case AuthTokenRequester::Result::kPostTouchAuthenticatorResponseInvalid:
  554. error = MakeCredentialStatus::kAuthenticatorResponseInvalid;
  555. break;
  556. case AuthTokenRequester::Result::kPostTouchAuthenticatorOperationDenied:
  557. error = MakeCredentialStatus::kUserConsentDenied;
  558. break;
  559. case AuthTokenRequester::Result::kPostTouchAuthenticatorPINSoftLock:
  560. error = MakeCredentialStatus::kSoftPINBlock;
  561. break;
  562. case AuthTokenRequester::Result::kPostTouchAuthenticatorPINHardLock:
  563. error = MakeCredentialStatus::kHardPINBlock;
  564. break;
  565. case AuthTokenRequester::Result::kSuccess:
  566. break;
  567. }
  568. // Pre touch events should be handled above.
  569. DCHECK_EQ(state_, State::kWaitingForToken);
  570. DCHECK_EQ(selected_authenticator_for_pin_uv_auth_token_, authenticator);
  571. if (error) {
  572. state_ = State::kFinished;
  573. std::move(completion_callback_).Run(*error, absl::nullopt, authenticator);
  574. return;
  575. }
  576. DCHECK_EQ(result, AuthTokenRequester::Result::kSuccess);
  577. auto request = std::make_unique<CtapMakeCredentialRequest>(request_);
  578. SpecializeRequestForAuthenticator(request.get(), authenticator);
  579. // If the authenticator supports biometric enrollment but is not enrolled,
  580. // offer enrollment with the request.
  581. if (authenticator->Options()->bio_enrollment_availability ==
  582. BioEnrollmentAvailability::kSupportedButUnprovisioned ||
  583. authenticator->Options()->bio_enrollment_availability_preview ==
  584. BioEnrollmentAvailability::kSupportedButUnprovisioned) {
  585. state_ = State::kBioEnrollment;
  586. bio_enroller_ =
  587. std::make_unique<BioEnroller>(this, authenticator, *token_response);
  588. bio_enrollment_complete_barrier_.emplace(base::BarrierClosure(
  589. 2, base::BindOnce(&MakeCredentialRequestHandler::OnEnrollmentComplete,
  590. weak_factory_.GetWeakPtr(), std::move(request))));
  591. observer()->StartBioEnrollment(
  592. base::BindOnce(&MakeCredentialRequestHandler::OnEnrollmentDismissed,
  593. weak_factory_.GetWeakPtr()));
  594. return;
  595. }
  596. DispatchRequestWithToken(authenticator, std::move(request),
  597. std::move(*token_response));
  598. }
  599. void MakeCredentialRequestHandler::ObtainPINUVAuthToken(
  600. FidoAuthenticator* authenticator,
  601. bool skip_pin_touch,
  602. bool internal_uv_locked) {
  603. AuthTokenRequester::Options options;
  604. options.token_permissions =
  605. GetMakeCredentialRequestPermissions(authenticator);
  606. options.rp_id = request_.rp.id;
  607. options.skip_pin_touch = skip_pin_touch;
  608. options.internal_uv_locked = internal_uv_locked;
  609. auth_token_requester_map_.insert(
  610. {authenticator, std::make_unique<AuthTokenRequester>(
  611. this, authenticator, std::move(options))});
  612. auth_token_requester_map_.at(authenticator)->ObtainPINUVAuthToken();
  613. }
  614. void MakeCredentialRequestHandler::HandleResponse(
  615. FidoAuthenticator* authenticator,
  616. std::unique_ptr<CtapMakeCredentialRequest> request,
  617. base::ElapsedTimer request_timer,
  618. CtapDeviceResponseCode status,
  619. absl::optional<AuthenticatorMakeCredentialResponse> response) {
  620. DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
  621. if (state_ != State::kWaitingForTouch &&
  622. state_ != State::kWaitingForResponseWithToken) {
  623. FIDO_LOG(DEBUG) << "Ignoring response from "
  624. << authenticator->GetDisplayName()
  625. << " because no longer waiting for touch";
  626. return;
  627. }
  628. if (selected_authenticator_for_pin_uv_auth_token_ &&
  629. authenticator != selected_authenticator_for_pin_uv_auth_token_) {
  630. FIDO_LOG(DEBUG) << "Ignoring response from "
  631. << authenticator->GetDisplayName()
  632. << " because another authenticator was selected";
  633. return;
  634. }
  635. #if BUILDFLAG(IS_WIN)
  636. if (authenticator->GetType() == FidoAuthenticator::Type::kWinNative) {
  637. state_ = State::kFinished;
  638. if (status != CtapDeviceResponseCode::kSuccess) {
  639. std::move(completion_callback_)
  640. .Run(WinCtapDeviceResponseCodeToMakeCredentialStatus(status),
  641. absl::nullopt, authenticator);
  642. return;
  643. }
  644. if (!response ||
  645. !ResponseValid(*authenticator, *request, *response, options_)) {
  646. FIDO_LOG(ERROR)
  647. << "Failing make credential request due to bad response from "
  648. << authenticator->GetDisplayName();
  649. std::move(completion_callback_)
  650. .Run(MakeCredentialStatus::kWinNotAllowedError, absl::nullopt,
  651. authenticator);
  652. return;
  653. }
  654. CancelActiveAuthenticators(authenticator->GetId());
  655. response->attestation_should_be_filtered = suppress_attestation_;
  656. std::move(completion_callback_)
  657. .Run(WinCtapDeviceResponseCodeToMakeCredentialStatus(status),
  658. std::move(*response), authenticator);
  659. return;
  660. }
  661. #endif
  662. // If we requested UV from an authentiator without uvToken support, UV failed,
  663. // and the authenticator supports PIN, fall back to that.
  664. if (request->user_verification != UserVerificationRequirement::kDiscouraged &&
  665. !request->pin_auth &&
  666. (status == CtapDeviceResponseCode::kCtap2ErrPinAuthInvalid ||
  667. status == CtapDeviceResponseCode::kCtap2ErrPinRequired) &&
  668. authenticator->PINUVDispositionForMakeCredential(*request, observer()) ==
  669. PINUVDisposition::kNoTokenInternalUVPINFallback) {
  670. // Authenticators without uvToken support will return this error immediately
  671. // without user interaction when internal UV is locked.
  672. const base::TimeDelta response_time = request_timer.Elapsed();
  673. if (response_time < kMinExpectedAuthenticatorResponseTime) {
  674. FIDO_LOG(DEBUG) << "Authenticator is probably locked, response_time="
  675. << response_time;
  676. ObtainPINUVAuthToken(authenticator, /*skip_pin_touch=*/false,
  677. /*internal_uv_locked=*/true);
  678. return;
  679. }
  680. ObtainPINUVAuthToken(authenticator, /*skip_pin_touch=*/true,
  681. /*internal_uv_locked=*/true);
  682. return;
  683. }
  684. if (options_.resident_key == ResidentKeyRequirement::kPreferred &&
  685. request->resident_key_required &&
  686. status == CtapDeviceResponseCode::kCtap2ErrKeyStoreFull) {
  687. FIDO_LOG(DEBUG) << "Downgrading rk=preferred to non-resident credential "
  688. "because key storage is full";
  689. request->resident_key_required = false;
  690. CtapMakeCredentialRequest request_copy(*request);
  691. authenticator->MakeCredential(
  692. std::move(request_copy), options_,
  693. base::BindOnce(&MakeCredentialRequestHandler::HandleResponse,
  694. weak_factory_.GetWeakPtr(), authenticator,
  695. std::move(request), base::ElapsedTimer()));
  696. return;
  697. }
  698. if (status == CtapDeviceResponseCode::kCtap2ErrUnsupportedAlgorithm) {
  699. // The authenticator didn't support any of the requested public-key
  700. // algorithms. This status will have been returned immediately.
  701. // Collect a touch and tell the user that it's unsupported.
  702. authenticator->GetTouch(base::BindOnce(
  703. &MakeCredentialRequestHandler::HandleInapplicableAuthenticator,
  704. weak_factory_.GetWeakPtr(), authenticator,
  705. MakeCredentialStatus::kNoCommonAlgorithms));
  706. return;
  707. }
  708. const absl::optional<MakeCredentialStatus> maybe_result =
  709. ConvertDeviceResponseCode(status);
  710. if (!maybe_result) {
  711. if (state_ == State::kWaitingForResponseWithToken) {
  712. std::move(completion_callback_)
  713. .Run(MakeCredentialStatus::kAuthenticatorResponseInvalid,
  714. absl::nullopt, authenticator);
  715. } else {
  716. FIDO_LOG(ERROR) << "Ignoring status " << static_cast<int>(status)
  717. << " from " << authenticator->GetDisplayName();
  718. }
  719. return;
  720. }
  721. state_ = State::kFinished;
  722. CancelActiveAuthenticators(authenticator->GetId());
  723. if (status != CtapDeviceResponseCode::kSuccess) {
  724. FIDO_LOG(ERROR) << "Failing make credential request due to status "
  725. << static_cast<int>(status) << " from "
  726. << authenticator->GetDisplayName();
  727. std::move(completion_callback_)
  728. .Run(*maybe_result, absl::nullopt, authenticator);
  729. return;
  730. }
  731. if (!response ||
  732. !ResponseValid(*authenticator, *request, *response, options_)) {
  733. FIDO_LOG(ERROR)
  734. << "Failing make credential request due to bad response from "
  735. << authenticator->GetDisplayName();
  736. std::move(completion_callback_)
  737. .Run(MakeCredentialStatus::kAuthenticatorResponseInvalid, absl::nullopt,
  738. authenticator);
  739. return;
  740. }
  741. if (authenticator->AuthenticatorTransport()) {
  742. base::UmaHistogramEnumeration(
  743. "WebAuthentication.MakeCredentialResponseTransport",
  744. *authenticator->AuthenticatorTransport());
  745. }
  746. response->attestation_should_be_filtered = suppress_attestation_;
  747. std::move(completion_callback_)
  748. .Run(MakeCredentialStatus::kSuccess, std::move(*response), authenticator);
  749. }
  750. void MakeCredentialRequestHandler::HandleExcludedAuthenticator(
  751. FidoAuthenticator* authenticator) {
  752. // User touched an authenticator that contains an AppID-based excluded
  753. // credential.
  754. state_ = State::kFinished;
  755. CancelActiveAuthenticators(authenticator->GetId());
  756. std::move(completion_callback_)
  757. .Run(MakeCredentialStatus::kUserConsentButCredentialExcluded,
  758. absl::nullopt, nullptr);
  759. }
  760. void MakeCredentialRequestHandler::HandleInapplicableAuthenticator(
  761. FidoAuthenticator* authenticator,
  762. MakeCredentialStatus status) {
  763. // User touched an authenticator that cannot handle this request.
  764. DCHECK_NE(status, MakeCredentialStatus::kSuccess);
  765. state_ = State::kFinished;
  766. CancelActiveAuthenticators(authenticator->GetId());
  767. std::move(completion_callback_).Run(status, absl::nullopt, nullptr);
  768. }
  769. void MakeCredentialRequestHandler::OnSampleCollected(
  770. BioEnrollmentSampleStatus status,
  771. int samples_remaining) {
  772. observer()->OnSampleCollected(samples_remaining);
  773. }
  774. void MakeCredentialRequestHandler::OnEnrollmentDone(
  775. absl::optional<std::vector<uint8_t>> template_id) {
  776. state_ = State::kBioEnrollmentDone;
  777. bio_enrollment_complete_barrier_->Run();
  778. }
  779. void MakeCredentialRequestHandler::OnEnrollmentError(
  780. CtapDeviceResponseCode status) {
  781. bio_enroller_.reset();
  782. state_ = State::kFinished;
  783. std::move(completion_callback_)
  784. .Run(MakeCredentialStatus::kAuthenticatorResponseInvalid, absl::nullopt,
  785. nullptr);
  786. }
  787. void MakeCredentialRequestHandler::OnEnrollmentDismissed() {
  788. if (state_ != State::kBioEnrollmentDone) {
  789. // There is still an inflight enrollment request. Cancel it.
  790. bio_enroller_->Cancel();
  791. }
  792. bio_enrollment_complete_barrier_->Run();
  793. }
  794. void MakeCredentialRequestHandler::OnEnrollmentComplete(
  795. std::unique_ptr<CtapMakeCredentialRequest> request) {
  796. DCHECK(state_ == State::kBioEnrollmentDone);
  797. bio_enrollment_complete_barrier_.reset();
  798. auto token = bio_enroller_->token();
  799. FidoAuthenticator* authenticator = bio_enroller_->authenticator();
  800. DCHECK_EQ(authenticator, selected_authenticator_for_pin_uv_auth_token_);
  801. bio_enroller_.reset();
  802. DispatchRequestWithToken(authenticator, std::move(request), std::move(token));
  803. }
  804. void MakeCredentialRequestHandler::DispatchRequestWithToken(
  805. FidoAuthenticator* authenticator,
  806. std::unique_ptr<CtapMakeCredentialRequest> request,
  807. pin::TokenResponse token) {
  808. observer()->FinishCollectToken();
  809. state_ = State::kWaitingForResponseWithToken;
  810. std::tie(request->pin_protocol, request->pin_auth) =
  811. token.PinAuth(request->client_data_hash);
  812. request->pin_token_for_exclude_list_probing = std::move(token);
  813. ReportMakeCredentialRequestTransport(authenticator);
  814. auto request_copy(*request.get()); // can't copy and move in the same stmt.
  815. authenticator->MakeCredential(
  816. std::move(request_copy), options_,
  817. base::BindOnce(&MakeCredentialRequestHandler::HandleResponse,
  818. weak_factory_.GetWeakPtr(), authenticator,
  819. std::move(request), base::ElapsedTimer()));
  820. }
  821. void MakeCredentialRequestHandler::SpecializeRequestForAuthenticator(
  822. CtapMakeCredentialRequest* request,
  823. const FidoAuthenticator* authenticator) {
  824. #if BUILDFLAG(IS_CHROMEOS)
  825. if (authenticator->AuthenticatorTransport() ==
  826. FidoTransportProtocol::kInternal &&
  827. options_.authenticator_attachment ==
  828. AuthenticatorAttachment::kCrossPlatform) {
  829. request->resident_key_required = false;
  830. request->user_verification = UserVerificationRequirement::kDiscouraged;
  831. // None of the other options below are applicable.
  832. return;
  833. }
  834. #endif
  835. // Only Windows cares about |authenticator_attachment| on the request.
  836. request->authenticator_attachment = options_.authenticator_attachment;
  837. const absl::optional<AuthenticatorSupportedOptions>&
  838. auth_options_empty_on_win = authenticator->Options();
  839. switch (options_.resident_key) {
  840. case ResidentKeyRequirement::kRequired:
  841. request->resident_key_required = true;
  842. break;
  843. case ResidentKeyRequirement::kPreferred: {
  844. // Create a resident key if the authenticator supports it, has sufficient
  845. // storage space for another credential, and we can obtain UV via client
  846. // PIN or an internal modality.
  847. request->resident_key_required =
  848. #if BUILDFLAG(IS_WIN)
  849. // Windows does not yet support rk=preferred.
  850. authenticator->GetType() != FidoAuthenticator::Type::kWinNative &&
  851. #endif
  852. auth_options_empty_on_win &&
  853. auth_options_empty_on_win->supports_resident_key &&
  854. !authenticator->DiscoverableCredentialStorageFull() &&
  855. (observer()->SupportsPIN() ||
  856. auth_options_empty_on_win->user_verification_availability ==
  857. AuthenticatorSupportedOptions::UserVerificationAvailability::
  858. kSupportedAndConfigured);
  859. break;
  860. }
  861. case ResidentKeyRequirement::kDiscouraged:
  862. request->resident_key_required = false;
  863. break;
  864. }
  865. switch (options_.large_blob_support) {
  866. case LargeBlobSupport::kRequired:
  867. request->large_blob_key = true;
  868. break;
  869. case LargeBlobSupport::kPreferred:
  870. request->large_blob_key =
  871. auth_options_empty_on_win &&
  872. auth_options_empty_on_win->supports_large_blobs &&
  873. request->resident_key_required;
  874. break;
  875. case LargeBlobSupport::kNotRequested:
  876. request->large_blob_key = false;
  877. break;
  878. }
  879. if (!options_.make_u2f_api_credential &&
  880. (request->resident_key_required ||
  881. (auth_options_empty_on_win && auth_options_empty_on_win->always_uv))) {
  882. request->user_verification = UserVerificationRequirement::kRequired;
  883. } else {
  884. request->user_verification = options_.user_verification;
  885. }
  886. if (options_.cred_protect_request &&
  887. authenticator->SupportsCredProtectExtension()) {
  888. request->cred_protect = CredProtectForAuthenticator(
  889. options_.cred_protect_request->first, *authenticator);
  890. request->cred_protect_enforce = options_.cred_protect_request->second;
  891. }
  892. if (request->hmac_secret && !authenticator->SupportsHMACSecretExtension()) {
  893. request->hmac_secret = false;
  894. }
  895. if (request->large_blob_key && auth_options_empty_on_win &&
  896. !auth_options_empty_on_win->supports_large_blobs) {
  897. request->large_blob_key = false;
  898. }
  899. if (request->min_pin_length_requested && auth_options_empty_on_win &&
  900. !auth_options_empty_on_win->supports_min_pin_length_extension) {
  901. request->min_pin_length_requested = false;
  902. }
  903. if (!authenticator->SupportsEnterpriseAttestation()) {
  904. switch (request->attestation_preference) {
  905. case AttestationConveyancePreference::kEnterpriseApprovedByBrowser:
  906. // If enterprise attestation is approved by policy then downgrade to
  907. // "direct" if not supported. Otherwise we have the strange behaviour
  908. // that kEnterpriseApprovedByBrowser turns into "none" on Windows
  909. // without EP support, or macOS/Chrome OS platform authenticators, but
  910. // "direct" elsewhere.
  911. request->attestation_preference =
  912. AttestationConveyancePreference::kDirect;
  913. break;
  914. case AttestationConveyancePreference::
  915. kEnterpriseIfRPListedOnAuthenticator:
  916. request->attestation_preference =
  917. AttestationConveyancePreference::kNone;
  918. break;
  919. default:
  920. break;
  921. }
  922. }
  923. if (request->cred_blob &&
  924. !authenticator->SupportsCredBlobOfSize(request->cred_blob->size())) {
  925. request->cred_blob.reset();
  926. }
  927. }
  928. } // namespace device