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 "content/browser/webauth/authenticator_common.h"
6 
7 #include <array>
8 #include <string>
9 #include <utility>
10 #include <vector>
11 
12 #include "base/bind.h"
13 #include "base/check.h"
14 #include "base/command_line.h"
15 #include "base/metrics/histogram_macros.h"
16 #include "base/notreached.h"
17 #include "base/rand_util.h"
18 #include "base/strings/string_piece.h"
19 #include "base/strings/utf_string_conversion_utils.h"
20 #include "base/task/task_traits.h"
21 #include "base/task/thread_pool.h"
22 #include "base/timer/timer.h"
23 #include "build/build_config.h"
24 #include "content/browser/bad_message.h"
25 #include "content/browser/webauth/authenticator_environment_impl.h"
26 #include "content/browser/webauth/virtual_authenticator_request_delegate.h"
27 #include "content/browser/webauth/virtual_fido_discovery_factory.h"
28 #include "content/browser/webauth/webauth_request_security_checker.h"
29 #include "content/public/browser/browser_context.h"
30 #include "content/public/browser/content_browser_client.h"
31 #include "content/public/browser/device_service.h"
32 #include "content/public/browser/is_uvpaa.h"
33 #include "content/public/browser/navigation_handle.h"
34 #include "content/public/browser/render_frame_host.h"
35 #include "content/public/browser/render_process_host.h"
36 #include "content/public/browser/render_widget_host_view.h"
37 #include "content/public/browser/web_contents.h"
38 #include "content/public/common/content_client.h"
39 #include "content/public/common/content_features.h"
40 #include "crypto/sha2.h"
41 #include "device/base/features.h"
42 #include "device/fido/attestation_statement.h"
43 #include "device/fido/ctap_make_credential_request.h"
44 #include "device/fido/features.h"
45 #include "device/fido/fido_authenticator.h"
46 #include "device/fido/fido_constants.h"
47 #include "device/fido/fido_parsing_utils.h"
48 #include "device/fido/fido_transport_protocol.h"
49 #include "device/fido/get_assertion_request_handler.h"
50 #include "device/fido/make_credential_request_handler.h"
51 #include "device/fido/public_key.h"
52 #include "device/fido/public_key_credential_descriptor.h"
53 #include "device/fido/public_key_credential_params.h"
54 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
55 #include "net/cert/asn1_util.h"
56 #include "net/der/input.h"
57 #include "net/der/parse_values.h"
58 #include "net/der/parser.h"
59 #include "services/network/public/cpp/is_potentially_trustworthy.h"
60 #include "third_party/blink/public/common/loader/network_utils.h"
61 #include "url/url_constants.h"
62 #include "url/url_util.h"
63 
64 #if defined(OS_MAC)
65 #include "device/fido/mac/authenticator.h"
66 #include "device/fido/mac/credential_metadata.h"
67 #endif
68 
69 #if defined(OS_WIN)
70 #include "device/fido/win/authenticator.h"
71 #include "device/fido/win/webauthn_api.h"
72 #endif
73 
74 namespace content {
75 
76 // RequestExtension is a type of extension in a WebAuthn request that might
77 // yield an extension output in the response.
78 enum class RequestExtension {
79   kAppID,
80   kHMACSecret,
81   kPRF,
82   kCredProps,
83   kLargeBlobEnable,
84   kLargeBlobRead,
85   kLargeBlobWrite,
86 };
87 
88 namespace client_data {
89 const char kCreateType[] = "webauthn.create";
90 const char kGetType[] = "webauthn.get";
91 const char kU2fSignType[] = "navigator.id.getAssertion";
92 const char kU2fRegisterType[] = "navigator.id.finishEnrollment";
93 }  // namespace client_data
94 
95 namespace {
96 
97 // AttestationPromptResult enumerates events related to attestation prompts.
98 // These values are recorded in an UMA histogram and so should not be
99 // reassigned.
100 enum class AttestationPromptResult {
101   // kQueried indicates that the embedder was queried in order to determine
102   // whether attestation information should be returned to the origin.
103   kQueried = 0,
104   // kTimeout indicates that a timeout occurred while awaiting the result of an
105   // attestation query.
106   kTimeout = 1,
107   // kAllowed indicates that the query to the embedder was resolved positively.
108   // (E.g. the user clicked to allow, or the embedded allowed immediately by
109   // policy.)
110   kAllowed = 2,
111   // kBlocked indicates that the query to the embedder was resolved negatively.
112   // (E.g. the user clicked to block, or closed the dialog.)
113   kBlocked = 3,
114   // kAbandoned indications that the user closed the tab or navigated away while
115   // the attestation prompt was showing.
116   kAbandoned = 4,
117   kMaxValue = kAbandoned,
118 };
119 
120 // Validates whether the given origin is authorized to use the provided App
121 // ID value, mostly according to the rules in
122 // https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-appid-and-facets-v1.2-ps-20170411.html#determining-if-a-caller-s-facetid-is-authorized-for-an-appid.
123 //
124 // Returns the App ID to use for the request, or base::nullopt if the origin
125 // is not authorized to use the provided value.
ProcessAppIdExtension(std::string appid,const url::Origin & origin)126 base::Optional<std::string> ProcessAppIdExtension(std::string appid,
127                                                   const url::Origin& origin) {
128   // The CryptoToken U2F extension checks the appid before calling the WebAuthn
129   // API so there is no need to validate it here.
130   if (WebAuthRequestSecurityChecker::OriginIsCryptoTokenExtension(origin)) {
131     if (!GURL(appid).is_valid()) {
132       DCHECK(false) << "cryptotoken request did not set a valid App ID";
133       return base::nullopt;
134     }
135     return appid;
136   }
137 
138   // Step 1: "If the AppID is not an HTTPS URL, and matches the FacetID of the
139   // caller, no additional processing is necessary and the operation may
140   // proceed."
141 
142   // Webauthn is only supported on secure origins and |ValidateEffectiveDomain|
143   // has already checked this property of |origin| before this call. Thus this
144   // step is moot.
145   DCHECK(blink::network_utils::IsOriginSecure(origin.GetURL()));
146 
147   // Step 2: "If the AppID is null or empty, the client must set the AppID to be
148   // the FacetID of the caller, and the operation may proceed without additional
149   // processing."
150   if (appid.empty()) {
151     // While the U2F spec says to default the App ID to the Facet ID, which is
152     // the origin plus a trailing forward slash [1], cryptotoken and Firefox
153     // just use the site's Origin without trailing slash. We follow their
154     // implementations rather than the spec.
155     //
156     // [1]https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-appid-and-facets-v2.0-id-20180227.html#determining-the-facetid-of-a-calling-application
157     appid = origin.Serialize();
158   }
159 
160   // Step 3: "If the caller's FacetID is an https:// Origin sharing the same
161   // host as the AppID, (e.g. if an application hosted at
162   // https://fido.example.com/myApp set an AppID of
163   // https://fido.example.com/myAppId), no additional processing is necessary
164   // and the operation may proceed."
165   GURL appid_url = GURL(appid);
166   if (!appid_url.is_valid() || appid_url.scheme() != url::kHttpsScheme ||
167       appid_url.scheme_piece() != origin.scheme()) {
168     WebAuthRequestSecurityChecker::ReportSecurityCheckFailure(
169         RelyingPartySecurityCheckFailure::kAppIdExtensionInvalid);
170     return base::nullopt;
171   }
172 
173   // This check is repeated inside |SameDomainOrHost|, just after this. However
174   // it's cheap and mirrors the structure of the spec.
175   if (appid_url.host_piece() == origin.host()) {
176     return appid;
177   }
178 
179   // At this point we diverge from the specification in order to avoid the
180   // complexity of making a network request which isn't believed to be
181   // necessary in practice. See also
182   // https://bugzilla.mozilla.org/show_bug.cgi?id=1244959#c8
183   if (net::registry_controlled_domains::SameDomainOrHost(
184           appid_url, origin,
185           net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
186     return appid;
187   }
188 
189   // As a compatibility hack, sites within google.com are allowed to assert two
190   // special-case AppIDs. Firefox also does this:
191   // https://groups.google.com/forum/#!msg/mozilla.dev.platform/Uiu3fwnA2xw/201ynAiPAQAJ
192   const GURL kGstatic1 =
193       GURL("https://www.gstatic.com/securitykey/origins.json");
194   const GURL kGstatic2 =
195       GURL("https://www.gstatic.com/securitykey/a/google.com/origins.json");
196   DCHECK(kGstatic1.is_valid() && kGstatic2.is_valid());
197 
198   if (origin.DomainIs("google.com") && !appid_url.has_ref() &&
199       (appid_url.EqualsIgnoringRef(kGstatic1) ||
200        appid_url.EqualsIgnoringRef(kGstatic2))) {
201     return appid;
202   }
203 
204   WebAuthRequestSecurityChecker::ReportSecurityCheckFailure(
205       RelyingPartySecurityCheckFailure::kAppIdExtensionDomainMismatch);
206 
207   return base::nullopt;
208 }
209 
210 // The application parameter is the SHA-256 hash of the UTF-8 encoding of
211 // the application identity (i.e. relying_party_id) of the application
212 // requesting the registration.
CreateApplicationParameter(const std::string & relying_party_id)213 std::array<uint8_t, crypto::kSHA256Length> CreateApplicationParameter(
214     const std::string& relying_party_id) {
215   std::array<uint8_t, crypto::kSHA256Length> application_parameter;
216   crypto::SHA256HashString(relying_party_id, application_parameter.data(),
217                            application_parameter.size());
218   return application_parameter;
219 }
220 
CreateCtapGetAssertionRequest(const std::string & client_data_json,const blink::mojom::PublicKeyCredentialRequestOptionsPtr & options,base::Optional<std::string> app_id,bool is_incognito)221 device::CtapGetAssertionRequest CreateCtapGetAssertionRequest(
222     const std::string& client_data_json,
223     const blink::mojom::PublicKeyCredentialRequestOptionsPtr& options,
224     base::Optional<std::string> app_id,
225     bool is_incognito) {
226   device::CtapGetAssertionRequest request_parameter(options->relying_party_id,
227                                                     client_data_json);
228 
229   request_parameter.allow_list = options->allow_credentials;
230 
231   request_parameter.user_verification = options->user_verification;
232 
233   if (app_id) {
234     request_parameter.alternative_application_parameter =
235         CreateApplicationParameter(*app_id);
236     request_parameter.app_id = std::move(*app_id);
237   }
238 
239   if (!options->cable_authentication_data.empty()) {
240     request_parameter.cable_extension = options->cable_authentication_data;
241   }
242   if (options->large_blob_read) {
243     request_parameter.large_blob_read = true;
244     request_parameter.large_blob_key = true;
245   }
246   if (options->large_blob_write) {
247     request_parameter.large_blob_key = true;
248   }
249   request_parameter.is_incognito_mode = is_incognito;
250   return request_parameter;
251 }
252 
253 // Parses the FIDO transport types extension from the DER-encoded, X.509
254 // certificate in |der_cert| and appends any unique transport types found to
255 // |out_transports|.
AppendUniqueTransportsFromCertificate(base::span<const uint8_t> der_cert,std::vector<device::FidoTransportProtocol> * out_transports)256 void AppendUniqueTransportsFromCertificate(
257     base::span<const uint8_t> der_cert,
258     std::vector<device::FidoTransportProtocol>* out_transports) {
259   // See
260   // https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-authenticator-transports-extension-v1.2-ps-20170411.html#fido-u2f-certificate-transports-extension
261   static constexpr uint8_t kTransportTypesOID[] = {
262       0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xe5, 0x1c, 0x02, 0x01, 0x01};
263   bool present, critical;
264   base::StringPiece contents;
265   if (!net::asn1::ExtractExtensionFromDERCert(
266           base::StringPiece(reinterpret_cast<const char*>(der_cert.data()),
267                             der_cert.size()),
268           base::StringPiece(reinterpret_cast<const char*>(kTransportTypesOID),
269                             sizeof(kTransportTypesOID)),
270           &present, &critical, &contents) ||
271       !present) {
272     return;
273   }
274 
275   const net::der::Input contents_der(contents);
276   net::der::Parser contents_parser(contents_der);
277   net::der::BitString transport_bits;
278   if (!contents_parser.ReadBitString(&transport_bits)) {
279     return;
280   }
281 
282   // The certificate extension contains a BIT STRING where different bits
283   // indicate support for different transports. The following array maps
284   // between these bit indexes and the FidoTransportProtocol enum.
285   static constexpr struct {
286     uint8_t bit_index;
287     device::FidoTransportProtocol transport;
288   } kTransportMapping[] = {
289       // Bit 0 is "Bluetooth Classic", not BLE. Since webauthn doesn't define a
290       // transport type for this we ignore it.
291       {1, device::FidoTransportProtocol::kBluetoothLowEnergy},
292       {2, device::FidoTransportProtocol::kUsbHumanInterfaceDevice},
293       {3, device::FidoTransportProtocol::kNearFieldCommunication},
294       {4, device::FidoTransportProtocol::kInternal},
295   };
296 
297   for (const auto& mapping : kTransportMapping) {
298     if (transport_bits.AssertsBit(mapping.bit_index) &&
299         !base::Contains(*out_transports, mapping.transport)) {
300       out_transports->push_back(mapping.transport);
301     }
302   }
303 }
304 
305 enum class AttestationErasureOption {
306   kIncludeAttestation,
307   kEraseAttestationButIncludeAaguid,
308   kEraseAttestationAndAaguid,
309 };
310 
AdjustTimeout(base::Optional<base::TimeDelta> timeout,RenderFrameHost * render_frame_host)311 base::TimeDelta AdjustTimeout(base::Optional<base::TimeDelta> timeout,
312                               RenderFrameHost* render_frame_host) {
313   // Time to wait for an authenticator to successfully complete an operation.
314   static constexpr base::TimeDelta kAdjustedTimeoutLower =
315       base::TimeDelta::FromSeconds(10);
316   static constexpr base::TimeDelta kAdjustedTimeoutUpper =
317       base::TimeDelta::FromMinutes(10);
318 
319   if (!timeout) {
320     return kAdjustedTimeoutUpper;
321   }
322   const bool testing_api_enabled =
323       AuthenticatorEnvironmentImpl::GetInstance()
324           ->IsVirtualAuthenticatorEnabledFor(
325               static_cast<RenderFrameHostImpl*>(render_frame_host)
326                   ->frame_tree_node());
327   if (testing_api_enabled) {
328     return *timeout;
329   }
330   return std::max(kAdjustedTimeoutLower,
331                   std::min(kAdjustedTimeoutUpper, *timeout));
332 }
333 
334 blink::mojom::MakeCredentialAuthenticatorResponsePtr
CreateMakeCredentialResponse(const std::string & client_data_json,device::AuthenticatorMakeCredentialResponse response_data,AttestationErasureOption attestation_erasure,const base::flat_set<RequestExtension> & requested_extensions)335 CreateMakeCredentialResponse(
336     const std::string& client_data_json,
337     device::AuthenticatorMakeCredentialResponse response_data,
338     AttestationErasureOption attestation_erasure,
339     const base::flat_set<RequestExtension>& requested_extensions) {
340   auto response = blink::mojom::MakeCredentialAuthenticatorResponse::New();
341   auto common_info = blink::mojom::CommonCredentialInfo::New();
342   common_info->client_data_json.assign(client_data_json.begin(),
343                                        client_data_json.end());
344   common_info->authenticator_data = response_data.attestation_object()
345                                         .authenticator_data()
346                                         .SerializeToByteArray();
347   if (response_data.android_client_data_ext()) {
348     DCHECK(base::FeatureList::IsEnabled(device::kWebAuthPhoneSupport));
349     common_info->client_data_json = *response_data.android_client_data_ext();
350   }
351   common_info->raw_id = response_data.raw_credential_id();
352   common_info->id = response_data.GetId();
353   response->info = std::move(common_info);
354 
355   // The transport list must not contain duplicates but the order doesn't matter
356   // because Blink will sort the resulting strings before returning them.
357   std::vector<device::FidoTransportProtocol> transports;
358   if (response_data.transport_used()) {
359     transports.push_back(*response_data.transport_used());
360   }
361   // If the attestation certificate specifies that the token supports any other
362   // transports, include them in the list.
363   base::Optional<base::span<const uint8_t>> leaf_cert =
364       response_data.attestation_object()
365           .attestation_statement()
366           .GetLeafCertificate();
367   if (leaf_cert) {
368     AppendUniqueTransportsFromCertificate(*leaf_cert, &transports);
369   }
370 
371   response->transports = std::move(transports);
372 
373   bool did_create_hmac_secret = false;
374   const base::Optional<cbor::Value>& maybe_extensions =
375       response_data.attestation_object().authenticator_data().extensions();
376   if (maybe_extensions) {
377     DCHECK(maybe_extensions->is_map());
378     const cbor::Value::MapValue& extensions = maybe_extensions->GetMap();
379     const auto hmac_secret_it =
380         extensions.find(cbor::Value(device::kExtensionHmacSecret));
381     if (hmac_secret_it != extensions.end() &&
382         hmac_secret_it->second.is_bool() && hmac_secret_it->second.GetBool()) {
383       did_create_hmac_secret = true;
384     }
385   }
386 
387   for (const RequestExtension ext : requested_extensions) {
388     switch (ext) {
389       case RequestExtension::kPRF:
390         response->echo_prf = true;
391         response->prf = did_create_hmac_secret;
392         break;
393       case RequestExtension::kHMACSecret:
394         response->echo_hmac_create_secret = true;
395         response->hmac_create_secret = did_create_hmac_secret;
396         break;
397       case RequestExtension::kCredProps:
398         response->echo_cred_props = true;
399         if (response_data.is_resident_key) {
400           response->has_cred_props_rk = true;
401           response->cred_props_rk = *response_data.is_resident_key;
402         }
403         break;
404       case RequestExtension::kLargeBlobEnable:
405         response->echo_large_blob = true;
406         response->supports_large_blob =
407             response_data.large_blob_key().has_value();
408         break;
409       case RequestExtension::kAppID:
410       case RequestExtension::kLargeBlobRead:
411       case RequestExtension::kLargeBlobWrite:
412         NOTREACHED();
413         break;
414     }
415   }
416 
417   switch (attestation_erasure) {
418     case AttestationErasureOption::kIncludeAttestation:
419       break;
420     case AttestationErasureOption::kEraseAttestationButIncludeAaguid:
421       response_data.EraseAttestationStatement(
422           device::AttestationObject::AAGUID::kInclude);
423       break;
424     case AttestationErasureOption::kEraseAttestationAndAaguid:
425       response_data.EraseAttestationStatement(
426           device::AttestationObject::AAGUID::kErase);
427       break;
428   }
429   response->attestation_object =
430       response_data.GetCBOREncodedAttestationObject();
431 
432   const device::PublicKey* public_key = response_data.attestation_object()
433                                             .authenticator_data()
434                                             .attested_data()
435                                             ->public_key();
436   response->public_key_algo = public_key->algorithm;
437   const base::Optional<std::vector<uint8_t>>& public_key_der =
438       public_key->der_bytes;
439   if (public_key_der) {
440     response->public_key_der.emplace(public_key_der.value());
441   }
442 
443   return response;
444 }
445 
CreateGetAssertionResponse(const std::string & client_data_json,device::AuthenticatorGetAssertionResponse response_data,const base::Optional<std::string> & app_id,const base::flat_set<RequestExtension> & requested_extensions)446 blink::mojom::GetAssertionAuthenticatorResponsePtr CreateGetAssertionResponse(
447     const std::string& client_data_json,
448     device::AuthenticatorGetAssertionResponse response_data,
449     const base::Optional<std::string>& app_id,
450     const base::flat_set<RequestExtension>& requested_extensions) {
451   auto response = blink::mojom::GetAssertionAuthenticatorResponse::New();
452   auto common_info = blink::mojom::CommonCredentialInfo::New();
453   common_info->client_data_json.assign(client_data_json.begin(),
454                                        client_data_json.end());
455   if (response_data.android_client_data_ext()) {
456     DCHECK(base::FeatureList::IsEnabled(device::kWebAuthPhoneSupport));
457     common_info->client_data_json = *response_data.android_client_data_ext();
458   }
459   common_info->raw_id = response_data.raw_credential_id();
460   common_info->id = response_data.GetId();
461   response->info = std::move(common_info);
462   response->info->authenticator_data =
463       response_data.auth_data().SerializeToByteArray();
464   response->signature = response_data.signature();
465   response_data.user_entity()
466       ? response->user_handle.emplace(response_data.user_entity()->id)
467       : response->user_handle.emplace();
468 
469   for (RequestExtension ext : requested_extensions) {
470     switch (ext) {
471       case RequestExtension::kAppID:
472         DCHECK(app_id);
473         response->echo_appid_extension = true;
474         if (response_data.GetRpIdHash() ==
475             CreateApplicationParameter(*app_id)) {
476           response->appid_extension = true;
477         }
478         break;
479       case RequestExtension::kPRF: {
480         response->echo_prf = true;
481         base::Optional<base::span<const uint8_t>> hmac_secret =
482             response_data.hmac_secret();
483         if (hmac_secret) {
484           auto prf_values = blink::mojom::PRFValues::New();
485           DCHECK(hmac_secret->size() == 32 || hmac_secret->size() == 64);
486           prf_values->first = device::fido_parsing_utils::Materialize(
487               hmac_secret->subspan(0, 32));
488           if (hmac_secret->size() == 64) {
489             prf_values->second = device::fido_parsing_utils::Materialize(
490                 hmac_secret->subspan(32, 32));
491           }
492           response->prf_results = std::move(prf_values);
493         } else {
494           response->prf_not_evaluated =
495               response_data.hmac_secret_not_evaluated();
496         }
497         break;
498       }
499       case RequestExtension::kLargeBlobRead:
500         response->echo_large_blob = true;
501         response->large_blob = response_data.large_blob();
502         break;
503       case RequestExtension::kLargeBlobWrite:
504         response->echo_large_blob = true;
505         response->echo_large_blob_written = true;
506         response->large_blob_written = response_data.large_blob_written();
507         break;
508       case RequestExtension::kHMACSecret:
509       case RequestExtension::kCredProps:
510       case RequestExtension::kLargeBlobEnable:
511         NOTREACHED();
512         break;
513     }
514   }
515 
516   return response;
517 }
518 
IsUserVerifyingPlatformAuthenticatorAvailableImpl(AuthenticatorRequestClientDelegate * delegate,device::FidoDiscoveryFactory * discovery_factory,BrowserContext * browser_context,blink::mojom::Authenticator::IsUserVerifyingPlatformAuthenticatorAvailableCallback callback)519 void IsUserVerifyingPlatformAuthenticatorAvailableImpl(
520     AuthenticatorRequestClientDelegate* delegate,
521     device::FidoDiscoveryFactory* discovery_factory,
522     BrowserContext* browser_context,
523     blink::mojom::Authenticator::
524         IsUserVerifyingPlatformAuthenticatorAvailableCallback callback) {
525   if (!delegate) {
526     // TODO(crbug/1110081): Investigate why this can be nullptr.
527     NOTREACHED();
528     std::move(callback).Run(false);
529     return;
530   }
531 
532   base::Optional<bool> is_uvpaa_override =
533       delegate->IsUserVerifyingPlatformAuthenticatorAvailableOverride();
534   if (is_uvpaa_override) {
535     std::move(callback).Run(*is_uvpaa_override);
536     return;
537   }
538 
539 #if defined(OS_MAC)
540   const base::Optional<device::fido::mac::AuthenticatorConfig> config =
541       delegate->GetTouchIdAuthenticatorConfig();
542   std::move(callback).Run(config &&
543                           IsUVPlatformAuthenticatorAvailable(*config));
544   return;
545 #elif defined(OS_WIN)
546   if (browser_context->IsOffTheRecord()) {
547     std::move(callback).Run(false);
548     return;
549   }
550 
551   std::move(callback).Run(IsUVPlatformAuthenticatorAvailable(
552       discovery_factory->win_webauthn_api()));
553   return;
554 #elif defined(OS_CHROMEOS)
555   if (browser_context->IsOffTheRecord()) {
556     std::move(callback).Run(false);
557     return;
558   }
559 
560   // ChromeOS needs to do a dbus call to determine platform authenticator
561   // availability. The call is fast in practice, but nonetheless may
562   // theoretically block.
563   base::ThreadPool::PostTaskAndReplyWithResult(
564       FROM_HERE, {base::TaskPriority::USER_BLOCKING, base::MayBlock()},
565       base::BindOnce(&IsUVPlatformAuthenticatorAvailable), std::move(callback));
566   return;
567 #else
568   std::move(callback).Run(false);
569   return;
570 #endif
571 }
572 
573 // GetAvailableTransports returns the set of transports that should be passed to
574 // a FidoRequestHandler for the current request. This determines for which
575 // transports the request handler will attempt to obtain FidoDiscovery
576 // instances.
GetAvailableTransports(RenderFrameHost * render_frame_host,AuthenticatorRequestClientDelegate * delegate,device::FidoDiscoveryFactory * discovery_factory,const url::Origin & caller_origin)577 base::flat_set<device::FidoTransportProtocol> GetAvailableTransports(
578     RenderFrameHost* render_frame_host,
579     AuthenticatorRequestClientDelegate* delegate,
580     device::FidoDiscoveryFactory* discovery_factory,
581     const url::Origin& caller_origin) {
582   // U2F requests proxied from the cryptotoken extension are limited to USB
583   // devices.
584   if (WebAuthRequestSecurityChecker::OriginIsCryptoTokenExtension(
585           caller_origin)) {
586     return base::flat_set<device::FidoTransportProtocol>(
587         {device::FidoTransportProtocol::kUsbHumanInterfaceDevice});
588   }
589 
590   base::flat_set<device::FidoTransportProtocol> transports;
591   transports.insert(device::FidoTransportProtocol::kUsbHumanInterfaceDevice);
592 
593 #if BUILDFLAG(IS_CHROMEOS_ASH)
594   // TODO(crbug.com/1157651): Work around CrOS platform authenticator being
595   // unavailable in Incognito.
596   if (!content::WebContents::FromRenderFrameHost(render_frame_host)
597            ->GetBrowserContext()
598            ->IsOffTheRecord()) {
599     transports.insert(device::FidoTransportProtocol::kInternal);
600   }
601 #else
602   transports.insert(device::FidoTransportProtocol::kInternal);
603 #endif
604 
605   if (discovery_factory->IsTestOverride()) {
606     // The desktop implementation does not support BLE or NFC, but we emulate
607     // them if the testing API is enabled.
608     transports.insert(device::FidoTransportProtocol::kBluetoothLowEnergy);
609     transports.insert(device::FidoTransportProtocol::kNearFieldCommunication);
610   }
611 
612   if (base::FeatureList::IsEnabled(features::kWebAuthCable) ||
613       base::FeatureList::IsEnabled(device::kWebAuthPhoneSupport)) {
614     transports.insert(
615         device::FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy);
616   }
617 
618   if (base::FeatureList::IsEnabled(device::kWebAuthPhoneSupport)) {
619     transports.insert(device::FidoTransportProtocol::kAndroidAccessory);
620   }
621 
622   return transports;
623 }
624 
625 // Returns a new FidoDiscoveryFactory for the current request. This may be
626 // a factory for virtual authenticators if the testing API is enabled for the
627 // given frame.
MakeDiscoveryFactory(RenderFrameHost * render_frame_host,AuthenticatorRequestClientDelegate * request_delegate,bool is_u2f_api_request)628 std::unique_ptr<device::FidoDiscoveryFactory> MakeDiscoveryFactory(
629     RenderFrameHost* render_frame_host,
630     AuthenticatorRequestClientDelegate* request_delegate,
631     bool is_u2f_api_request) {
632   VirtualAuthenticatorManagerImpl* virtual_authenticator_manager =
633       AuthenticatorEnvironmentImpl::GetInstance()
634           ->MaybeGetVirtualAuthenticatorManager(
635               static_cast<RenderFrameHostImpl*>(render_frame_host)
636                   ->frame_tree_node());
637   if (virtual_authenticator_manager) {
638     return virtual_authenticator_manager->MakeDiscoveryFactory();
639   }
640 
641   auto discovery_factory = std::make_unique<device::FidoDiscoveryFactory>();
642 #if defined(OS_MAC)
643   discovery_factory->set_mac_touch_id_info(
644       request_delegate->GetTouchIdAuthenticatorConfig());
645 #endif  // defined(OS_MAC)
646 
647 #if defined(OS_WIN)
648   if (base::FeatureList::IsEnabled(device::kWebAuthUseNativeWinApi)) {
649     discovery_factory->set_win_webauthn_api(
650         device::WinWebAuthnApi::GetDefault());
651   }
652 #endif  // defined(OS_WIN)
653 
654 #if defined(OS_CHROMEOS)
655   if (base::FeatureList::IsEnabled(device::kWebAuthCrosPlatformAuthenticator)) {
656     discovery_factory->set_generate_request_id_callback(
657         request_delegate->GetGenerateRequestIdCallback(render_frame_host));
658   }
659 #endif  // defined(OS_CHROMEOS)
660 
661   return discovery_factory;
662 }
663 
664 }  // namespace
665 
AuthenticatorCommon(RenderFrameHost * render_frame_host)666 AuthenticatorCommon::AuthenticatorCommon(RenderFrameHost* render_frame_host)
667     : render_frame_host_(render_frame_host),
668       security_checker_(static_cast<RenderFrameHostImpl*>(render_frame_host)
669                             ->GetWebAuthRequestSecurityChecker()) {
670   DCHECK(render_frame_host_);
671   // Disable the back-forward cache for any document that makes WebAuthn
672   // requests. Pages using privacy-sensitive APIs are generally exempt from
673   // back-forward cache for now as a precaution.
674   BackForwardCache::DisableForRenderFrameHost(render_frame_host,
675                                               "WebAuthenticationAPI");
676 }
677 
~AuthenticatorCommon()678 AuthenticatorCommon::~AuthenticatorCommon() {
679   // This call exists to assert that |render_frame_host_| outlives this object.
680   // If this is violated, ASAN should notice.
681   render_frame_host_->GetRoutingID();
682 }
683 
684 std::unique_ptr<AuthenticatorRequestClientDelegate>
CreateRequestDelegate()685 AuthenticatorCommon::CreateRequestDelegate() {
686   auto* frame_tree_node =
687       static_cast<RenderFrameHostImpl*>(render_frame_host_)->frame_tree_node();
688   if (AuthenticatorEnvironmentImpl::GetInstance()
689           ->IsVirtualAuthenticatorEnabledFor(frame_tree_node)) {
690     return std::make_unique<VirtualAuthenticatorRequestDelegate>(
691         frame_tree_node);
692   }
693   return GetContentClient()->browser()->GetWebAuthenticationRequestDelegate(
694       render_frame_host_);
695 }
696 
StartMakeCredentialRequest(bool allow_skipping_pin_touch)697 void AuthenticatorCommon::StartMakeCredentialRequest(
698     bool allow_skipping_pin_touch) {
699   InitDiscoveryFactory();
700 
701   request_delegate_->ConfigureCable(
702       caller_origin_, base::span<const device::CableDiscoveryData>(),
703       discovery_factory());
704 
705   make_credential_options_->allow_skipping_pin_touch = allow_skipping_pin_touch;
706 
707   request_ = std::make_unique<device::MakeCredentialRequestHandler>(
708       discovery_factory(),
709       GetAvailableTransports(render_frame_host_, request_delegate_.get(),
710                              discovery_factory(), caller_origin_),
711       *ctap_make_credential_request_, *make_credential_options_,
712       base::BindOnce(&AuthenticatorCommon::OnRegisterResponse,
713                      weak_factory_.GetWeakPtr()));
714 
715   request_delegate_->RegisterActionCallbacks(
716       base::BindOnce(&AuthenticatorCommon::OnCancelFromUI,
717                      weak_factory_.GetWeakPtr()) /* cancel_callback */,
718       base::BindRepeating(
719           &AuthenticatorCommon::StartMakeCredentialRequest,
720           weak_factory_.GetWeakPtr(),
721           /*allow_skipping_pin_touch=*/false) /* start_over_callback */,
722       base::BindRepeating(
723           &device::FidoRequestHandlerBase::StartAuthenticatorRequest,
724           request_->GetWeakPtr()) /* request_callback */,
725       base::BindRepeating(
726           &device::FidoRequestHandlerBase::PowerOnBluetoothAdapter,
727           request_->GetWeakPtr()) /* bluetooth_adapter_power_on_callback */);
728   if (make_credential_options_->resident_key !=
729       device::ResidentKeyRequirement::kDiscouraged) {
730     request_delegate_->SetMightCreateResidentCredential(true);
731   }
732   request_->set_observer(request_delegate_.get());
733 }
734 
StartGetAssertionRequest(bool allow_skipping_pin_touch)735 void AuthenticatorCommon::StartGetAssertionRequest(
736     bool allow_skipping_pin_touch) {
737   InitDiscoveryFactory();
738 
739   base::span<const device::CableDiscoveryData> cable_pairings;
740   if (ctap_get_assertion_request_->cable_extension && IsFocused()) {
741     cable_pairings = *ctap_get_assertion_request_->cable_extension;
742   }
743   request_delegate_->ConfigureCable(caller_origin_, cable_pairings,
744                                     discovery_factory());
745 
746   request_ = std::make_unique<device::GetAssertionRequestHandler>(
747       discovery_factory(),
748       GetAvailableTransports(render_frame_host_, request_delegate_.get(),
749                              discovery_factory(), caller_origin_),
750       *ctap_get_assertion_request_, *ctap_get_assertion_options_,
751       allow_skipping_pin_touch,
752       base::BindOnce(&AuthenticatorCommon::OnSignResponse,
753                      weak_factory_.GetWeakPtr()));
754 
755   request_delegate_->RegisterActionCallbacks(
756       base::BindOnce(&AuthenticatorCommon::OnCancelFromUI,
757                      weak_factory_.GetWeakPtr()) /* cancel_callback */,
758       base::BindRepeating(
759           &AuthenticatorCommon::StartGetAssertionRequest,
760           weak_factory_.GetWeakPtr(),
761           /*allow_skipping_pin_touch=*/false) /* start_over_callback */,
762       base::BindRepeating(
763           &device::FidoRequestHandlerBase::StartAuthenticatorRequest,
764           request_->GetWeakPtr()) /* request_callback */,
765       base::BindRepeating(
766           &device::FidoRequestHandlerBase::PowerOnBluetoothAdapter,
767           request_->GetWeakPtr()) /* bluetooth_adapter_power_on_callback */);
768 
769   request_->set_observer(request_delegate_.get());
770 }
771 
IsFocused() const772 bool AuthenticatorCommon::IsFocused() const {
773   return render_frame_host_->IsCurrent() && request_delegate_->IsFocused();
774 }
775 
OnLargeBlobCompressed(data_decoder::DataDecoder::ResultOrError<mojo_base::BigBuffer> result)776 void AuthenticatorCommon::OnLargeBlobCompressed(
777     data_decoder::DataDecoder::ResultOrError<mojo_base::BigBuffer> result) {
778   ctap_get_assertion_request_->large_blob_write =
779       device::fido_parsing_utils::MaterializeOrNull(result.value);
780   StartGetAssertionRequest(/*allow_skipping_pin_touch=*/true);
781 }
782 
OnLargeBlobUncompressed(device::AuthenticatorGetAssertionResponse response,data_decoder::DataDecoder::ResultOrError<mojo_base::BigBuffer> result)783 void AuthenticatorCommon::OnLargeBlobUncompressed(
784     device::AuthenticatorGetAssertionResponse response,
785     data_decoder::DataDecoder::ResultOrError<mojo_base::BigBuffer> result) {
786   response.set_large_blob(
787       device::fido_parsing_utils::MaterializeOrNull(result.value));
788   InvokeCallbackAndCleanup(
789       std::move(get_assertion_response_callback_),
790       blink::mojom::AuthenticatorStatus::SUCCESS,
791       CreateGetAssertionResponse(client_data_json_, std::move(response),
792                                  app_id_, requested_extensions_));
793 }
794 
795 // static
796 // mojom::Authenticator
MakeCredential(url::Origin caller_origin,blink::mojom::PublicKeyCredentialCreationOptionsPtr options,blink::mojom::Authenticator::MakeCredentialCallback callback)797 void AuthenticatorCommon::MakeCredential(
798     url::Origin caller_origin,
799     blink::mojom::PublicKeyCredentialCreationOptionsPtr options,
800     blink::mojom::Authenticator::MakeCredentialCallback callback) {
801   if (request_) {
802     if (WebAuthRequestSecurityChecker::OriginIsCryptoTokenExtension(
803             caller_origin)) {
804       // Requests originating from cryptotoken will generally outlive any
805       // navigation events on the tab of the request's sender. Evict pending
806       // requests if cryptotoken sends a new one such that requests from before
807       // a navigation event do not prevent new requests. See
808       // https://crbug.com/935480.
809       CancelWithStatus(blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
810     } else {
811       std::move(callback).Run(
812           blink::mojom::AuthenticatorStatus::PENDING_REQUEST, nullptr);
813       return;
814     }
815   }
816   DCHECK(!request_);
817 
818   bool is_cross_origin;
819   blink::mojom::AuthenticatorStatus status =
820       security_checker_->ValidateAncestorOrigins(
821           caller_origin,
822           WebAuthRequestSecurityChecker::RequestType::kMakeCredential,
823           &is_cross_origin);
824   if (status != blink::mojom::AuthenticatorStatus::SUCCESS) {
825     InvokeCallbackAndCleanup(std::move(callback), status);
826     return;
827   }
828 
829   request_delegate_ = CreateRequestDelegate();
830   if (!request_delegate_) {
831     InvokeCallbackAndCleanup(std::move(callback),
832                              blink::mojom::AuthenticatorStatus::PENDING_REQUEST,
833                              nullptr, Focus::kDontCheck);
834     return;
835   }
836 
837   base::Optional<std::string> rp_id =
838       request_delegate_->MaybeGetRelyingPartyIdOverride(
839           options->relying_party.id, caller_origin);
840 
841   if (!rp_id) {
842     // If the delegate didn't override RP ID selection then apply standard
843     // rules.
844     rp_id = std::move(options->relying_party.id);
845     status = security_checker_->ValidateDomainAndRelyingPartyID(caller_origin,
846                                                                 *rp_id);
847     if (status != blink::mojom::AuthenticatorStatus::SUCCESS) {
848       InvokeCallbackAndCleanup(std::move(callback), status, nullptr,
849                                Focus::kDontCheck);
850       return;
851     }
852   }
853 
854   caller_origin_ = caller_origin;
855   relying_party_id_ = *rp_id;
856   options->relying_party.id = std::move(*rp_id);
857   request_delegate_->SetRelyingPartyId(relying_party_id_);
858 
859   base::Optional<std::string> appid_exclude;
860   if (options->appid_exclude) {
861     appid_exclude =
862         ProcessAppIdExtension(*options->appid_exclude, caller_origin);
863     if (!appid_exclude) {
864       InvokeCallbackAndCleanup(
865           std::move(callback),
866           blink::mojom::AuthenticatorStatus::INVALID_DOMAIN, nullptr,
867           Focus::kDontCheck);
868       return;
869     }
870   }
871 
872   if (options->user.icon_url) {
873     status = security_checker_->ValidateAPrioriAuthenticatedUrl(
874         *options->user.icon_url);
875   }
876   if (status == blink::mojom::AuthenticatorStatus::SUCCESS &&
877       options->relying_party.icon_url) {
878     status = security_checker_->ValidateAPrioriAuthenticatedUrl(
879         *options->relying_party.icon_url);
880   }
881   if (status != blink::mojom::AuthenticatorStatus::SUCCESS) {
882     bad_message::ReceivedBadMessage(render_frame_host_->GetProcess(),
883                                     bad_message::AUTH_INVALID_ICON_URL);
884     InvokeCallbackAndCleanup(std::move(callback), status, nullptr,
885                              Focus::kDontCheck);
886     return;
887   }
888 
889   if (!IsFocused()) {
890     InvokeCallbackAndCleanup(std::move(callback),
891                              blink::mojom::AuthenticatorStatus::NOT_FOCUSED);
892     return;
893   }
894 
895   const device::AuthenticatorSelectionCriteria
896       authenticator_selection_criteria =
897           options->authenticator_selection
898               ? *options->authenticator_selection
899               : device::AuthenticatorSelectionCriteria();
900   make_credential_options_ = device::MakeCredentialRequestHandler::Options(
901       authenticator_selection_criteria);
902 
903   const bool might_create_resident_key =
904       make_credential_options_->resident_key !=
905       device::ResidentKeyRequirement::kDiscouraged;
906   if (might_create_resident_key && !request_delegate_->SupportsResidentKeys()) {
907     if (make_credential_options_->resident_key ==
908         device::ResidentKeyRequirement::kRequired) {
909       InvokeCallbackAndCleanup(
910           std::move(callback),
911           blink::mojom::AuthenticatorStatus::RESIDENT_CREDENTIALS_UNSUPPORTED);
912       return;
913     }
914     // Downgrade 'preferred' to 'discouraged'.
915     make_credential_options_->resident_key =
916         device::ResidentKeyRequirement::kDiscouraged;
917   }
918 
919   // Reject any non-sensical credProtect extension values.
920   if (  // Can't require the default policy (or no policy).
921       (options->enforce_protection_policy &&
922        (options->protection_policy ==
923             blink::mojom::ProtectionPolicy::UNSPECIFIED ||
924         options->protection_policy == blink::mojom::ProtectionPolicy::NONE)) ||
925       // For non-resident keys, NONE doesn't make sense. (UV_OR_CRED_ID_REQUIRED
926       // does because, with CTAP 2.0, just because a resident key isn't
927       // _required_ doesn't mean that one won't be created and an RP might want
928       // credProtect to take effect if that happens.)
929       (!might_create_resident_key &&
930        options->protection_policy == blink::mojom::ProtectionPolicy::NONE) ||
931       // UV_REQUIRED only makes sense if UV is required overall.
932       (options->protection_policy ==
933            blink::mojom::ProtectionPolicy::UV_REQUIRED &&
934        authenticator_selection_criteria.user_verification_requirement() !=
935            device::UserVerificationRequirement::kRequired)) {
936     InvokeCallbackAndCleanup(
937         std::move(callback),
938         blink::mojom::AuthenticatorStatus::PROTECTION_POLICY_INCONSISTENT);
939     return;
940   }
941 
942   base::Optional<device::CredProtectRequest> cred_protect_request;
943   switch (options->protection_policy) {
944     case blink::mojom::ProtectionPolicy::UNSPECIFIED:
945       if (might_create_resident_key) {
946         // If not specified, kUVOrCredIDRequired is made the default unless
947         // the authenticator defaults to something better.
948         cred_protect_request =
949             device::CredProtectRequest::kUVOrCredIDRequiredOrBetter;
950       }
951       break;
952     case blink::mojom::ProtectionPolicy::NONE:
953       cred_protect_request = device::CredProtectRequest::kUVOptional;
954       break;
955     case blink::mojom::ProtectionPolicy::UV_OR_CRED_ID_REQUIRED:
956       cred_protect_request = device::CredProtectRequest::kUVOrCredIDRequired;
957       break;
958     case blink::mojom::ProtectionPolicy::UV_REQUIRED:
959       cred_protect_request = device::CredProtectRequest::kUVRequired;
960       break;
961   }
962 
963   if (cred_protect_request) {
964     make_credential_options_->cred_protect_request = {
965         {*cred_protect_request, options->enforce_protection_policy}};
966   }
967 
968   DCHECK(make_credential_response_callback_.is_null());
969   make_credential_response_callback_ = std::move(callback);
970 
971   timer_->Start(
972       FROM_HERE, AdjustTimeout(options->timeout, render_frame_host_),
973       base::BindOnce(&AuthenticatorCommon::OnTimeout, base::Unretained(this)));
974 
975   const bool origin_is_crypto_token_extension =
976       WebAuthRequestSecurityChecker::OriginIsCryptoTokenExtension(
977           caller_origin_);
978 
979   // Cryptotoken provides the sender origin for register requests in the
980   // |relying_party| |name| attribute. (The |id| attribute contains the AppID.)
981   client_data_json_ =
982       origin_is_crypto_token_extension
983           ? device::SerializeCollectedClientDataToJson(
984                 client_data::kU2fRegisterType, *options->relying_party.name,
985                 options->challenge, /*is_cross_origin=*/false,
986                 /*use_legacy_u2f_type_key=*/true)
987           : device::SerializeCollectedClientDataToJson(
988                 client_data::kCreateType, caller_origin_.Serialize(),
989                 options->challenge, is_cross_origin);
990 
991   // Cryptotoken requests should be proxied without UI.
992   if (origin_is_crypto_token_extension || disable_ui_)
993     request_delegate_->DisableUI();
994 
995   UMA_HISTOGRAM_COUNTS_100(
996       "WebAuthentication.MakeCredentialExcludeCredentialsCount",
997       options->exclude_credentials.size());
998 
999   ctap_make_credential_request_ = device::CtapMakeCredentialRequest(
1000       client_data_json_, options->relying_party, options->user,
1001       device::PublicKeyCredentialParams(options->public_key_parameters));
1002   ctap_make_credential_request_->exclude_list = options->exclude_credentials;
1003   if (options->prf_enable) {
1004     requested_extensions_.insert(RequestExtension::kPRF);
1005     ctap_make_credential_request_->hmac_secret = true;
1006   }
1007   if (options->hmac_create_secret) {
1008     requested_extensions_.insert(RequestExtension::kHMACSecret);
1009     ctap_make_credential_request_->hmac_secret = true;
1010   }
1011   if (options->cred_props) {
1012     requested_extensions_.insert(RequestExtension::kCredProps);
1013   }
1014   if (options->large_blob_enable != device::LargeBlobSupport::kNotRequested) {
1015     requested_extensions_.insert(RequestExtension::kLargeBlobEnable);
1016   }
1017   make_credential_options_->large_blob_support = options->large_blob_enable;
1018   ctap_make_credential_request_->app_id = std::move(appid_exclude);
1019   ctap_make_credential_request_->is_incognito_mode =
1020       browser_context()->IsOffTheRecord();
1021   // On dual protocol CTAP2/U2F devices, force credential creation over U2F.
1022   ctap_make_credential_request_->is_u2f_only = origin_is_crypto_token_extension;
1023 
1024   if (base::FeatureList::IsEnabled(device::kWebAuthPhoneSupport) &&
1025       !origin_is_crypto_token_extension && !is_cross_origin) {
1026     // Send the unhashed origin and challenge to caBLEv2 authenticators, because
1027     // the Android API requires them. It does not accept clientDataJSON or its
1028     // hash.
1029     // NOTE: Because Android has no way of building a clientDataJSON for
1030     // cross-origin requests, we don't create the extension for those. This
1031     // problem will go away once we add clientDataHash inputs to Android.
1032     make_credential_options_->android_client_data_ext.emplace(
1033         client_data::kCreateType, caller_origin_, options->challenge);
1034   }
1035 
1036   // Compute the effective attestation conveyance preference.
1037   device::AttestationConveyancePreference attestation = options->attestation;
1038   // Enterprise attestation should not have been approved by this point.
1039   DCHECK(attestation !=
1040          device::AttestationConveyancePreference::kEnterpriseApprovedByBrowser);
1041   if (attestation == device::AttestationConveyancePreference::
1042                          kEnterpriseIfRPListedOnAuthenticator &&
1043       request_delegate_->ShouldPermitIndividualAttestation(relying_party_id_)) {
1044     attestation =
1045         device::AttestationConveyancePreference::kEnterpriseApprovedByBrowser;
1046   }
1047   ctap_make_credential_request_->attestation_preference = attestation;
1048 
1049   StartMakeCredentialRequest(/*allow_skipping_pin_touch=*/true);
1050 }
1051 
1052 // mojom:Authenticator
GetAssertion(url::Origin caller_origin,blink::mojom::PublicKeyCredentialRequestOptionsPtr options,blink::mojom::Authenticator::GetAssertionCallback callback)1053 void AuthenticatorCommon::GetAssertion(
1054     url::Origin caller_origin,
1055     blink::mojom::PublicKeyCredentialRequestOptionsPtr options,
1056     blink::mojom::Authenticator::GetAssertionCallback callback) {
1057   if (request_) {
1058     if (WebAuthRequestSecurityChecker::OriginIsCryptoTokenExtension(
1059             caller_origin)) {
1060       // Requests originating from cryptotoken will generally outlive any
1061       // navigation events on the tab of the request's sender. Evict pending
1062       // requests if cryptotoken sends a new one such that requests from before
1063       // a navigation event do not prevent new requests. See
1064       // https://crbug.com/935480.
1065       CancelWithStatus(blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1066     } else {
1067       std::move(callback).Run(
1068           blink::mojom::AuthenticatorStatus::PENDING_REQUEST, nullptr);
1069       return;
1070     }
1071   }
1072   DCHECK(!request_);
1073 
1074   bool is_cross_origin;
1075   blink::mojom::AuthenticatorStatus status =
1076       security_checker_->ValidateAncestorOrigins(
1077           caller_origin,
1078           WebAuthRequestSecurityChecker::RequestType::kGetAssertion,
1079           &is_cross_origin);
1080   if (status != blink::mojom::AuthenticatorStatus::SUCCESS) {
1081     InvokeCallbackAndCleanup(std::move(callback), status);
1082     return;
1083   }
1084 
1085   request_delegate_ = CreateRequestDelegate();
1086   if (!request_delegate_) {
1087     InvokeCallbackAndCleanup(std::move(callback),
1088                              blink::mojom::AuthenticatorStatus::PENDING_REQUEST,
1089                              nullptr);
1090     return;
1091   }
1092 
1093   base::Optional<std::string> rp_id =
1094       request_delegate_->MaybeGetRelyingPartyIdOverride(
1095           options->relying_party_id, caller_origin);
1096 
1097   if (!rp_id) {
1098     // If the delegate didn't override RP ID selection then apply standard
1099     // rules.
1100     status = security_checker_->ValidateDomainAndRelyingPartyID(
1101         caller_origin, options->relying_party_id);
1102     if (status != blink::mojom::AuthenticatorStatus::SUCCESS) {
1103       InvokeCallbackAndCleanup(std::move(callback), status, nullptr);
1104       return;
1105     }
1106 
1107     rp_id = std::move(options->relying_party_id);
1108   }
1109 
1110   caller_origin_ = caller_origin;
1111   relying_party_id_ = *rp_id;
1112   options->relying_party_id = std::move(*rp_id);
1113   request_delegate_->SetRelyingPartyId(relying_party_id_);
1114 
1115   const bool origin_is_crypto_token_extension =
1116       WebAuthRequestSecurityChecker::OriginIsCryptoTokenExtension(
1117           caller_origin_);
1118 
1119   // Cryptotoken provides the sender origin for U2F sign requests in the
1120   // |relying_party_id| attribute.
1121   client_data_json_ =
1122       origin_is_crypto_token_extension
1123           ? device::SerializeCollectedClientDataToJson(
1124                 client_data::kU2fSignType, options->relying_party_id,
1125                 options->challenge, /*is_cross_origin=*/false,
1126                 /*use_legacy_u2f_type_key=*/true)
1127           : device::SerializeCollectedClientDataToJson(
1128                 client_data::kGetType, caller_origin_.Serialize(),
1129                 options->challenge, is_cross_origin);
1130 
1131   // Cryptotoken requests should be proxied without UI.
1132   if (origin_is_crypto_token_extension || disable_ui_)
1133     request_delegate_->DisableUI();
1134 
1135   if (options->allow_credentials.empty()) {
1136     if (!request_delegate_->SupportsResidentKeys()) {
1137       InvokeCallbackAndCleanup(
1138           std::move(callback),
1139           blink::mojom::AuthenticatorStatus::RESIDENT_CREDENTIALS_UNSUPPORTED);
1140       return;
1141     }
1142     empty_allow_list_ = true;
1143   }
1144 
1145   if (options->appid) {
1146     requested_extensions_.insert(RequestExtension::kAppID);
1147     app_id_ = ProcessAppIdExtension(*options->appid, caller_origin_);
1148     if (!app_id_) {
1149       InvokeCallbackAndCleanup(
1150           std::move(callback),
1151           blink::mojom::AuthenticatorStatus::INVALID_DOMAIN);
1152       return;
1153     }
1154   }
1155 
1156   if (options->large_blob_read && options->large_blob_write) {
1157     InvokeCallbackAndCleanup(
1158         std::move(callback),
1159         blink::mojom::AuthenticatorStatus::CANNOT_READ_AND_WRITE_LARGE_BLOB);
1160     return;
1161   }
1162 
1163   if (options->large_blob_read) {
1164     requested_extensions_.insert(RequestExtension::kLargeBlobRead);
1165   } else if (options->large_blob_write) {
1166     if (options->allow_credentials.size() != 1) {
1167       InvokeCallbackAndCleanup(std::move(callback),
1168                                blink::mojom::AuthenticatorStatus::
1169                                    INVALID_ALLOW_CREDENTIALS_FOR_LARGE_BLOB);
1170       return;
1171     }
1172     requested_extensions_.insert(RequestExtension::kLargeBlobWrite);
1173   }
1174 
1175   UMA_HISTOGRAM_COUNTS_100(
1176       "WebAuthentication.CredentialRequestAllowCredentialsCount",
1177       options->allow_credentials.size());
1178 
1179   DCHECK(get_assertion_response_callback_.is_null());
1180   get_assertion_response_callback_ = std::move(callback);
1181 
1182   timer_->Start(
1183       FROM_HERE, AdjustTimeout(options->timeout, render_frame_host_),
1184       base::BindOnce(&AuthenticatorCommon::OnTimeout, base::Unretained(this)));
1185 
1186   ctap_get_assertion_request_ = CreateCtapGetAssertionRequest(
1187       client_data_json_, options, app_id_, browser_context()->IsOffTheRecord());
1188   ctap_get_assertion_options_.emplace();
1189 
1190   bool is_first = true;
1191   base::Optional<std::vector<uint8_t>> last_id;
1192   if (options->prf) {
1193     requested_extensions_.insert(RequestExtension::kPRF);
1194     for (const auto& prf_input_from_renderer : options->prf_inputs) {
1195       device::CtapGetAssertionOptions::PRFInput prf_input;
1196 
1197       // This statement enforces invariants that should be established by the
1198       // renderer.
1199       if (
1200           // Only the first element in the vector may be the default.
1201           (!is_first && !prf_input_from_renderer->id) ||
1202           // The PRF inputs must be sorted by credential ID to show that there
1203           // are no duplicates.
1204           (last_id.has_value() && prf_input_from_renderer->id.has_value() &&
1205            *last_id >= *prf_input_from_renderer->id) ||
1206           // The lengths are specified in authenticator.mojom, so hopefully Mojo
1207           // enforces them too.
1208           prf_input_from_renderer->first.size() != prf_input.salt1.size() ||
1209           (prf_input_from_renderer->second &&
1210            prf_input_from_renderer->second->size() != prf_input.salt1.size())) {
1211         NOTREACHED();
1212 
1213         InvokeCallbackAndCleanup(
1214             std::move(get_assertion_response_callback_),
1215             blink::mojom::AuthenticatorStatus::UNKNOWN_ERROR);
1216         return;
1217       }
1218       is_first = false;
1219       last_id = prf_input_from_renderer->id;
1220 
1221       if (prf_input_from_renderer->id) {
1222         prf_input.credential_id = std::move(*prf_input_from_renderer->id);
1223       }
1224 
1225       memcpy(prf_input.salt1.data(), prf_input_from_renderer->first.data(),
1226              prf_input.salt1.size());
1227       if (prf_input_from_renderer->second) {
1228         prf_input.salt2.emplace();
1229         memcpy(prf_input.salt2->data(), prf_input_from_renderer->second->data(),
1230                prf_input.salt2->size());
1231       }
1232 
1233       ctap_get_assertion_options_->prf_inputs.emplace_back(
1234           std::move(prf_input));
1235     }
1236   }
1237 
1238   ctap_get_assertion_request_->is_u2f_only = origin_is_crypto_token_extension;
1239   if (base::FeatureList::IsEnabled(device::kWebAuthPhoneSupport) &&
1240       !origin_is_crypto_token_extension && !is_cross_origin) {
1241     // Send the unhashed origin and challenge to caBLEv2 authenticators, because
1242     // the Android API requires them. It does not accept clientDataJSON or its
1243     // hash.
1244     // NOTE: Because Android has no way of building a clientDataJSON for
1245     // cross-origin requests, we don't create the extension for those. This
1246     // problem will go away once we add clientDataHash inputs to Android.
1247     ctap_get_assertion_request_->android_client_data_ext.emplace(
1248         client_data::kGetType, caller_origin_, options->challenge);
1249   }
1250 
1251   if (options->large_blob_write) {
1252     data_decoder_.GzipCompress(
1253         *options->large_blob_write,
1254         base::BindOnce(&AuthenticatorCommon::OnLargeBlobCompressed,
1255                        weak_factory_.GetWeakPtr()));
1256     return;
1257   }
1258 
1259   StartGetAssertionRequest(/*allow_skipping_pin_touch=*/true);
1260 }
1261 
IsUserVerifyingPlatformAuthenticatorAvailable(blink::mojom::Authenticator::IsUserVerifyingPlatformAuthenticatorAvailableCallback callback)1262 void AuthenticatorCommon::IsUserVerifyingPlatformAuthenticatorAvailable(
1263     blink::mojom::Authenticator::
1264         IsUserVerifyingPlatformAuthenticatorAvailableCallback callback) {
1265   // Use |request_delegate_| if a request is currently in progress; or create a
1266   // temporary request delegate otherwise. Note that CreateRequestDelegate() may
1267   // return nullptr if there is an active |request_delegate_| already.
1268   std::unique_ptr<AuthenticatorRequestClientDelegate> maybe_request_delegate =
1269       request_delegate_ ? nullptr : CreateRequestDelegate();
1270   AuthenticatorRequestClientDelegate* request_delegate_ptr =
1271       request_delegate_ ? request_delegate_.get()
1272                         : maybe_request_delegate.get();
1273 
1274   std::unique_ptr<device::FidoDiscoveryFactory> discovery_factory =
1275       MakeDiscoveryFactory(render_frame_host_, request_delegate_ptr,
1276                            /*is_u2f_api_request=*/false);
1277   device::FidoDiscoveryFactory* discovery_factory_testing_override =
1278       AuthenticatorEnvironmentImpl::GetInstance()
1279           ->MaybeGetDiscoveryFactoryTestOverride();
1280   device::FidoDiscoveryFactory* discovery_factory_ptr =
1281       discovery_factory_testing_override ? discovery_factory_testing_override
1282                                          : discovery_factory.get();
1283 
1284   auto post_done_callback = base::BindOnce(
1285       [](blink::mojom::Authenticator::
1286              IsUserVerifyingPlatformAuthenticatorAvailableCallback callback,
1287          bool is_available) {
1288         base::SequencedTaskRunnerHandle::Get()->PostTask(
1289             FROM_HERE, base::BindOnce(std::move(callback), is_available));
1290       },
1291       std::move(callback));
1292 
1293   IsUserVerifyingPlatformAuthenticatorAvailableImpl(
1294       request_delegate_ptr, discovery_factory_ptr, browser_context(),
1295       std::move(post_done_callback));
1296 }
1297 
Cancel()1298 void AuthenticatorCommon::Cancel() {
1299   CancelWithStatus(blink::mojom::AuthenticatorStatus::ABORT_ERROR);
1300 }
1301 
1302 // Callback to handle the async registration response from a U2fDevice.
OnRegisterResponse(device::MakeCredentialStatus status_code,base::Optional<device::AuthenticatorMakeCredentialResponse> response_data,const device::FidoAuthenticator * authenticator)1303 void AuthenticatorCommon::OnRegisterResponse(
1304     device::MakeCredentialStatus status_code,
1305     base::Optional<device::AuthenticatorMakeCredentialResponse> response_data,
1306     const device::FidoAuthenticator* authenticator) {
1307   if (!request_) {
1308     // Either the callback was called immediately and |request_| has not yet
1309     // been assigned (this is a bug), or a navigation caused the request to be
1310     // canceled while a callback was enqueued.
1311     return;
1312   }
1313 
1314   switch (status_code) {
1315     case device::MakeCredentialStatus::kUserConsentButCredentialExcluded:
1316       // Duplicate registration: the new credential would be created on an
1317       // authenticator that already contains one of the credentials in
1318       // |exclude_credentials|.
1319       SignalFailureToRequestDelegate(
1320           authenticator,
1321           AuthenticatorRequestClientDelegate::InterestingFailureReason::
1322               kKeyAlreadyRegistered,
1323           blink::mojom::AuthenticatorStatus::CREDENTIAL_EXCLUDED);
1324       return;
1325     case device::MakeCredentialStatus::kAuthenticatorResponseInvalid:
1326       // The response from the authenticator was corrupted.
1327       InvokeCallbackAndCleanup(
1328           std::move(make_credential_response_callback_),
1329           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR, nullptr,
1330           Focus::kDoCheck);
1331       return;
1332     case device::MakeCredentialStatus::kUserConsentDenied:
1333       SignalFailureToRequestDelegate(
1334           authenticator,
1335           AuthenticatorRequestClientDelegate::InterestingFailureReason::
1336               kUserConsentDenied,
1337           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1338       return;
1339     case device::MakeCredentialStatus::kSoftPINBlock:
1340       SignalFailureToRequestDelegate(
1341           authenticator,
1342           AuthenticatorRequestClientDelegate::InterestingFailureReason::
1343               kSoftPINBlock,
1344           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1345       return;
1346     case device::MakeCredentialStatus::kHardPINBlock:
1347       SignalFailureToRequestDelegate(
1348           authenticator,
1349           AuthenticatorRequestClientDelegate::InterestingFailureReason::
1350               kHardPINBlock,
1351           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1352       return;
1353     case device::MakeCredentialStatus::kAuthenticatorRemovedDuringPINEntry:
1354       SignalFailureToRequestDelegate(
1355           authenticator,
1356           AuthenticatorRequestClientDelegate::InterestingFailureReason::
1357               kAuthenticatorRemovedDuringPINEntry,
1358           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1359       return;
1360     case device::MakeCredentialStatus::kAuthenticatorMissingResidentKeys:
1361       SignalFailureToRequestDelegate(
1362           authenticator,
1363           AuthenticatorRequestClientDelegate::InterestingFailureReason::
1364               kAuthenticatorMissingResidentKeys,
1365           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1366       return;
1367     case device::MakeCredentialStatus::kAuthenticatorMissingUserVerification:
1368       SignalFailureToRequestDelegate(
1369           authenticator,
1370           AuthenticatorRequestClientDelegate::InterestingFailureReason::
1371               kAuthenticatorMissingUserVerification,
1372           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1373       return;
1374     case device::MakeCredentialStatus::kAuthenticatorMissingLargeBlob:
1375       SignalFailureToRequestDelegate(
1376           authenticator,
1377           AuthenticatorRequestClientDelegate::InterestingFailureReason::
1378               kAuthenticatorMissingLargeBlob,
1379           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1380       return;
1381     case device::MakeCredentialStatus::kNoCommonAlgorithms:
1382       SignalFailureToRequestDelegate(
1383           authenticator,
1384           AuthenticatorRequestClientDelegate::InterestingFailureReason::
1385               kNoCommonAlgorithms,
1386           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1387       return;
1388     case device::MakeCredentialStatus::kStorageFull:
1389       SignalFailureToRequestDelegate(
1390           authenticator,
1391           AuthenticatorRequestClientDelegate::InterestingFailureReason::
1392               kStorageFull,
1393           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1394       return;
1395     case device::MakeCredentialStatus::kWinInvalidStateError:
1396       InvokeCallbackAndCleanup(
1397           std::move(make_credential_response_callback_),
1398           blink::mojom::AuthenticatorStatus::CREDENTIAL_EXCLUDED, nullptr,
1399           Focus::kDoCheck);
1400       return;
1401     case device::MakeCredentialStatus::kWinNotAllowedError:
1402       SignalFailureToRequestDelegate(
1403           authenticator,
1404           AuthenticatorRequestClientDelegate::InterestingFailureReason::
1405               kWinUserCancelled,
1406           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1407       return;
1408     case device::MakeCredentialStatus::kSuccess:
1409       DCHECK(response_data.has_value());
1410       DCHECK(authenticator);
1411 
1412       auto transport_used = authenticator->AuthenticatorTransport();
1413       if (transport_used) {
1414         request_delegate_->UpdateLastTransportUsed(*transport_used);
1415       }
1416       bool is_transport_used_internal =
1417           transport_used &&
1418           *transport_used == device::FidoTransportProtocol::kInternal;
1419 
1420       const auto attestation =
1421           ctap_make_credential_request_->attestation_preference;
1422       base::Optional<AttestationErasureOption> attestation_erasure;
1423       const bool origin_is_crypto_token_extension =
1424           WebAuthRequestSecurityChecker::OriginIsCryptoTokenExtension(
1425               caller_origin_);
1426 
1427       // cryptotoken checks the attestation blocklist itself.
1428       if (!origin_is_crypto_token_extension &&
1429           device::DoesMatchWebAuthAttestationBlockedDomains(caller_origin_) &&
1430           !request_delegate_->ShouldPermitIndividualAttestation(
1431               relying_party_id_)) {
1432         attestation_erasure =
1433             AttestationErasureOption::kEraseAttestationAndAaguid;
1434       } else if (origin_is_crypto_token_extension &&
1435                  attestation !=
1436                      device::AttestationConveyancePreference::kNone) {
1437         // Cryptotoken requests may bypass the attestation prompt because the
1438         // extension implements its own. Invoking the attestation prompt code
1439         // here would not work anyway, because the WebContents associated with
1440         // the extension is not associated with any tab and therefore cannot
1441         // draw modal dialogs for the UI.
1442         //
1443         // Note that for AttestationConveyancePreference::kNone, attestation
1444         // erasure is still performed as usual.
1445         attestation_erasure = AttestationErasureOption::kIncludeAttestation;
1446       } else if (attestation == device::AttestationConveyancePreference::
1447                                     kEnterpriseApprovedByBrowser) {
1448         // If enterprise attestation was approved by policy then it can be
1449         // returned immediately.
1450         attestation_erasure = AttestationErasureOption::kIncludeAttestation;
1451       } else if (attestation == device::AttestationConveyancePreference::
1452                                     kEnterpriseIfRPListedOnAuthenticator &&
1453                  !response_data->enterprise_attestation_returned) {
1454         // If enterprise attestation was requested, not approved by policy, and
1455         // not approved by the authenticator, then any attestation is stripped.
1456         attestation_erasure =
1457             AttestationErasureOption::kEraseAttestationAndAaguid;
1458       } else if (attestation !=
1459                  device::AttestationConveyancePreference::kNone) {
1460         UMA_HISTOGRAM_ENUMERATION("WebAuthentication.AttestationPromptResult",
1461                                   AttestationPromptResult::kQueried);
1462         awaiting_attestation_response_ = true;
1463         request_delegate_->ShouldReturnAttestation(
1464             relying_party_id_, authenticator,
1465             response_data->enterprise_attestation_returned,
1466             base::BindOnce(
1467                 &AuthenticatorCommon::OnRegisterResponseAttestationDecided,
1468                 weak_factory_.GetWeakPtr(), std::move(*response_data),
1469                 is_transport_used_internal));
1470       } else if (response_data->IsSelfAttestation()) {
1471         attestation_erasure = AttestationErasureOption::kIncludeAttestation;
1472       } else if (is_transport_used_internal) {
1473         // Contrary to what the WebAuthn spec says, for internal (platform)
1474         // authenticators we do not erase the AAGUID from authenticatorData,
1475         // even if requested attestationConveyancePreference is "none".
1476         attestation_erasure =
1477             AttestationErasureOption::kEraseAttestationButIncludeAaguid;
1478       } else {
1479         attestation_erasure =
1480             AttestationErasureOption::kEraseAttestationAndAaguid;
1481       }
1482 
1483       if (attestation_erasure.has_value()) {
1484         InvokeCallbackAndCleanup(
1485             std::move(make_credential_response_callback_),
1486             blink::mojom::AuthenticatorStatus::SUCCESS,
1487             CreateMakeCredentialResponse(
1488                 client_data_json_, std::move(*response_data),
1489                 *attestation_erasure, requested_extensions_),
1490             Focus::kDoCheck);
1491       }
1492 
1493       return;
1494   }
1495   NOTREACHED();
1496 }
1497 
OnRegisterResponseAttestationDecided(device::AuthenticatorMakeCredentialResponse response_data,bool is_transport_used_internal,bool attestation_permitted)1498 void AuthenticatorCommon::OnRegisterResponseAttestationDecided(
1499     device::AuthenticatorMakeCredentialResponse response_data,
1500     bool is_transport_used_internal,
1501     bool attestation_permitted) {
1502   awaiting_attestation_response_ = false;
1503   if (!request_) {
1504     // The request has already been cleaned up, probably because a navigation
1505     // occurred while the permissions prompt was pending.
1506     return;
1507   }
1508 
1509   AttestationErasureOption attestation_erasure;
1510   if (!attestation_permitted) {
1511     UMA_HISTOGRAM_ENUMERATION("WebAuthentication.AttestationPromptResult",
1512                               AttestationPromptResult::kBlocked);
1513     if (is_transport_used_internal) {
1514       // For internal (platform) authenticators, we do not erase the
1515       // AAGUID from authenticatorData even if the user declines to
1516       // share attestation.
1517       attestation_erasure =
1518           AttestationErasureOption::kEraseAttestationButIncludeAaguid;
1519     } else {
1520       attestation_erasure =
1521           AttestationErasureOption::kEraseAttestationAndAaguid;
1522     }
1523   } else {
1524     UMA_HISTOGRAM_ENUMERATION("WebAuthentication.AttestationPromptResult",
1525                               AttestationPromptResult::kAllowed);
1526     attestation_erasure = AttestationErasureOption::kIncludeAttestation;
1527   }
1528 
1529   // The check for IsAttestationCertificateInappropriatelyIdentifying is
1530   // performed after the permissions prompt, even though we know the answer
1531   // before, because this still effectively discloses the make & model of
1532   // the authenticator: If an RP sees a "none" attestation from Chrome after
1533   // requesting direct attestation then it knows that it was one of the
1534   // tokens with inappropriate certs.
1535   if (response_data.IsAttestationCertificateInappropriatelyIdentifying() &&
1536       !request_delegate_->ShouldPermitIndividualAttestation(
1537           relying_party_id_)) {
1538     // The attestation response is incorrectly individually identifiable, but
1539     // the consent is for make & model information about a token, not for
1540     // individually-identifiable information. Erase the attestation to stop it
1541     // begin a tracking signal.
1542 
1543     // The only way to get the underlying attestation will be to list the RP ID
1544     // in the enterprise policy, because that enables the individual attestation
1545     // bit in the register request and permits individual attestation generally.
1546     attestation_erasure = AttestationErasureOption::kEraseAttestationAndAaguid;
1547   }
1548 
1549   InvokeCallbackAndCleanup(
1550       std::move(make_credential_response_callback_),
1551       blink::mojom::AuthenticatorStatus::SUCCESS,
1552       CreateMakeCredentialResponse(client_data_json_, std::move(response_data),
1553                                    attestation_erasure, requested_extensions_),
1554       Focus::kDoCheck);
1555 }
1556 
OnSignResponse(device::GetAssertionStatus status_code,base::Optional<std::vector<device::AuthenticatorGetAssertionResponse>> response_data,const device::FidoAuthenticator * authenticator)1557 void AuthenticatorCommon::OnSignResponse(
1558     device::GetAssertionStatus status_code,
1559     base::Optional<std::vector<device::AuthenticatorGetAssertionResponse>>
1560         response_data,
1561     const device::FidoAuthenticator* authenticator) {
1562   DCHECK(!response_data || !response_data->empty());  // empty vector is invalid
1563 
1564   if (!request_) {
1565     // Either the callback was called immediately and |request_| has not yet
1566     // been assigned (this is a bug), or a navigation caused the request to be
1567     // canceled while a callback was enqueued.
1568     return;
1569   }
1570 
1571   switch (status_code) {
1572     case device::GetAssertionStatus::kUserConsentButCredentialNotRecognized:
1573       SignalFailureToRequestDelegate(
1574           authenticator,
1575           AuthenticatorRequestClientDelegate::InterestingFailureReason::
1576               kKeyNotRegistered,
1577           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1578       return;
1579     case device::GetAssertionStatus::kAuthenticatorResponseInvalid:
1580       // The response from the authenticator was corrupted.
1581       InvokeCallbackAndCleanup(
1582           std::move(get_assertion_response_callback_),
1583           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1584       return;
1585     case device::GetAssertionStatus::kUserConsentDenied:
1586       SignalFailureToRequestDelegate(
1587           authenticator,
1588           AuthenticatorRequestClientDelegate::InterestingFailureReason::
1589               kUserConsentDenied,
1590           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1591       return;
1592     case device::GetAssertionStatus::kSoftPINBlock:
1593       SignalFailureToRequestDelegate(
1594           authenticator,
1595           AuthenticatorRequestClientDelegate::InterestingFailureReason::
1596               kSoftPINBlock,
1597           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1598       return;
1599     case device::GetAssertionStatus::kHardPINBlock:
1600       SignalFailureToRequestDelegate(
1601           authenticator,
1602           AuthenticatorRequestClientDelegate::InterestingFailureReason::
1603               kHardPINBlock,
1604           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1605       return;
1606     case device::GetAssertionStatus::kAuthenticatorRemovedDuringPINEntry:
1607       SignalFailureToRequestDelegate(
1608           authenticator,
1609           AuthenticatorRequestClientDelegate::InterestingFailureReason::
1610               kAuthenticatorRemovedDuringPINEntry,
1611           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1612       return;
1613     case device::GetAssertionStatus::kAuthenticatorMissingResidentKeys:
1614       SignalFailureToRequestDelegate(
1615           authenticator,
1616           AuthenticatorRequestClientDelegate::InterestingFailureReason::
1617               kAuthenticatorMissingResidentKeys,
1618           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1619       return;
1620     case device::GetAssertionStatus::kAuthenticatorMissingUserVerification:
1621       SignalFailureToRequestDelegate(
1622           authenticator,
1623           AuthenticatorRequestClientDelegate::InterestingFailureReason::
1624               kAuthenticatorMissingUserVerification,
1625           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1626       return;
1627     case device::GetAssertionStatus::kWinNotAllowedError:
1628       SignalFailureToRequestDelegate(
1629           authenticator,
1630           AuthenticatorRequestClientDelegate::InterestingFailureReason::
1631               kWinUserCancelled,
1632           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1633       return;
1634     case device::GetAssertionStatus::kSuccess:
1635       DCHECK(response_data.has_value());
1636       DCHECK(authenticator);
1637 
1638       if (authenticator->AuthenticatorTransport()) {
1639         request_delegate_->UpdateLastTransportUsed(
1640             *authenticator->AuthenticatorTransport());
1641       }
1642 
1643       // Show an account picker for requests with empty allow lists.
1644       // Authenticators may omit the identifying information in the user entity
1645       // if only one credential matches, or if they have account selection UI
1646       // built-in. In that case, consider that credential pre-selected.
1647       if (empty_allow_list_ &&
1648           (response_data->size() > 1 ||
1649            (response_data->at(0).user_entity() &&
1650             (response_data->at(0).user_entity()->name ||
1651              response_data->at(0).user_entity()->display_name)))) {
1652         request_delegate_->SelectAccount(
1653             std::move(*response_data),
1654             base::BindOnce(&AuthenticatorCommon::OnAccountSelected,
1655                            weak_factory_.GetWeakPtr()));
1656       } else {
1657         OnAccountSelected(std::move(response_data->at(0)));
1658       }
1659       return;
1660   }
1661   NOTREACHED();
1662 }
1663 
OnAccountSelected(device::AuthenticatorGetAssertionResponse response)1664 void AuthenticatorCommon::OnAccountSelected(
1665     device::AuthenticatorGetAssertionResponse response) {
1666   if (response.large_blob()) {
1667     std::vector<uint8_t> blob = std::move(*response.large_blob());
1668     data_decoder_.GzipUncompress(
1669         blob, base::BindOnce(&AuthenticatorCommon::OnLargeBlobUncompressed,
1670                              weak_factory_.GetWeakPtr(), std::move(response)));
1671     return;
1672   }
1673   InvokeCallbackAndCleanup(
1674       std::move(get_assertion_response_callback_),
1675       blink::mojom::AuthenticatorStatus::SUCCESS,
1676       CreateGetAssertionResponse(client_data_json_, std::move(response),
1677                                  app_id_, requested_extensions_));
1678   return;
1679 }
1680 
SignalFailureToRequestDelegate(const::device::FidoAuthenticator * authenticator,AuthenticatorRequestClientDelegate::InterestingFailureReason reason,blink::mojom::AuthenticatorStatus status)1681 void AuthenticatorCommon::SignalFailureToRequestDelegate(
1682     const ::device::FidoAuthenticator* authenticator,
1683     AuthenticatorRequestClientDelegate::InterestingFailureReason reason,
1684     blink::mojom::AuthenticatorStatus status) {
1685   error_awaiting_user_acknowledgement_ = status;
1686 
1687   // The request has failed, but the UI may delay resolution of the request
1688   // callback and cleanup of the FidoRequestHandler and its associated
1689   // discoveries and authenticators. Tell them to stop processing the request in
1690   // the meantime.
1691   request_->StopDiscoveries();
1692   request_->CancelActiveAuthenticators();
1693 
1694   // If WebAuthnUi is enabled, this error blocks until after receiving user
1695   // acknowledgement. Otherwise, the error is returned right away.
1696   if (request_delegate_->DoesBlockRequestOnFailure(reason)) {
1697     return;
1698   }
1699   CancelWithStatus(error_awaiting_user_acknowledgement_);
1700 }  // namespace content
1701 
1702 // TODO(crbug.com/814418): Add web tests to verify timeouts are
1703 // indistinguishable from NOT_ALLOWED_ERROR cases.
OnTimeout()1704 void AuthenticatorCommon::OnTimeout() {
1705   DCHECK(request_delegate_);
1706   if (awaiting_attestation_response_) {
1707     UMA_HISTOGRAM_ENUMERATION("WebAuthentication.AttestationPromptResult",
1708                               AttestationPromptResult::kTimeout);
1709     awaiting_attestation_response_ = false;
1710   }
1711 
1712   SignalFailureToRequestDelegate(
1713       /*authenticator=*/nullptr,
1714       AuthenticatorRequestClientDelegate::InterestingFailureReason::kTimeout,
1715       blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1716 }
1717 
CancelWithStatus(blink::mojom::AuthenticatorStatus status)1718 void AuthenticatorCommon::CancelWithStatus(
1719     blink::mojom::AuthenticatorStatus status) {
1720   // If response callback is invoked already, then ignore cancel request.
1721   if (!make_credential_response_callback_ && !get_assertion_response_callback_)
1722     return;
1723   if (make_credential_response_callback_) {
1724     InvokeCallbackAndCleanup(std::move(make_credential_response_callback_),
1725                              status);
1726   } else if (get_assertion_response_callback_) {
1727     InvokeCallbackAndCleanup(std::move(get_assertion_response_callback_),
1728                              status);
1729   }
1730 }
1731 
OnCancelFromUI()1732 void AuthenticatorCommon::OnCancelFromUI() {
1733   CancelWithStatus(error_awaiting_user_acknowledgement_);
1734 }
1735 
InvokeCallbackAndCleanup(blink::mojom::Authenticator::MakeCredentialCallback callback,blink::mojom::AuthenticatorStatus status,blink::mojom::MakeCredentialAuthenticatorResponsePtr response,Focus check_focus)1736 void AuthenticatorCommon::InvokeCallbackAndCleanup(
1737     blink::mojom::Authenticator::MakeCredentialCallback callback,
1738     blink::mojom::AuthenticatorStatus status,
1739     blink::mojom::MakeCredentialAuthenticatorResponsePtr response,
1740     Focus check_focus) {
1741   if (check_focus != Focus::kDontCheck && !(request_delegate_ && IsFocused())) {
1742     std::move(callback).Run(blink::mojom::AuthenticatorStatus::NOT_FOCUSED,
1743                             nullptr);
1744   } else {
1745     std::move(callback).Run(status, std::move(response));
1746   }
1747 
1748   Cleanup();
1749 }
1750 
InvokeCallbackAndCleanup(blink::mojom::Authenticator::GetAssertionCallback callback,blink::mojom::AuthenticatorStatus status,blink::mojom::GetAssertionAuthenticatorResponsePtr response)1751 void AuthenticatorCommon::InvokeCallbackAndCleanup(
1752     blink::mojom::Authenticator::GetAssertionCallback callback,
1753     blink::mojom::AuthenticatorStatus status,
1754     blink::mojom::GetAssertionAuthenticatorResponsePtr response) {
1755   std::move(callback).Run(status, std::move(response));
1756   Cleanup();
1757 }
1758 
Cleanup()1759 void AuthenticatorCommon::Cleanup() {
1760   if (awaiting_attestation_response_) {
1761     UMA_HISTOGRAM_ENUMERATION("WebAuthentication.AttestationPromptResult",
1762                               AttestationPromptResult::kAbandoned);
1763     awaiting_attestation_response_ = false;
1764   }
1765 
1766   timer_->Stop();
1767   request_.reset();
1768   discovery_factory_.reset();
1769   discovery_factory_testing_override_ = nullptr;
1770   ctap_make_credential_request_.reset();
1771   make_credential_options_.reset();
1772   ctap_get_assertion_request_.reset();
1773   ctap_get_assertion_options_.reset();
1774   request_delegate_.reset();
1775   make_credential_response_callback_.Reset();
1776   get_assertion_response_callback_.Reset();
1777   client_data_json_.clear();
1778   app_id_.reset();
1779   caller_origin_ = url::Origin();
1780   relying_party_id_.clear();
1781   empty_allow_list_ = false;
1782   error_awaiting_user_acknowledgement_ =
1783       blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR;
1784   requested_extensions_.clear();
1785 }
1786 
DisableUI()1787 void AuthenticatorCommon::DisableUI() {
1788   disable_ui_ = true;
1789 }
1790 
browser_context() const1791 BrowserContext* AuthenticatorCommon::browser_context() const {
1792   return content::WebContents::FromRenderFrameHost(render_frame_host_)
1793       ->GetBrowserContext();
1794 }
1795 
discovery_factory()1796 device::FidoDiscoveryFactory* AuthenticatorCommon::discovery_factory() {
1797   DCHECK(discovery_factory_);
1798   return discovery_factory_testing_override_
1799              ? discovery_factory_testing_override_
1800              : discovery_factory_.get();
1801 }
1802 
InitDiscoveryFactory()1803 void AuthenticatorCommon::InitDiscoveryFactory() {
1804   const bool is_u2f_api_request =
1805       WebAuthRequestSecurityChecker::OriginIsCryptoTokenExtension(
1806           caller_origin_);
1807   discovery_factory_ = MakeDiscoveryFactory(
1808       render_frame_host_, request_delegate_.get(), is_u2f_api_request);
1809   // TODO(martinkr): |discovery_factory_testing_override_| is a long-lived
1810   // VirtualFidoDeviceDiscovery so that tests can maintain and alter virtual
1811   // authenticator state in between requests. We should extract a longer-lived
1812   // configuration object from VirtualFidoDeviceDiscovery, so we can simply
1813   // stick a short-lived instance into |discovery_factory_| and eliminate
1814   // |discovery_factory_testing_override_|.
1815   discovery_factory_testing_override_ =
1816       AuthenticatorEnvironmentImpl::GetInstance()
1817           ->MaybeGetDiscoveryFactoryTestOverride();
1818 }
1819 
1820 }  // namespace content
1821