| // Copyright 2021 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef DEVICE_FIDO_FILTER_H_ |
| #define DEVICE_FIDO_FILTER_H_ |
| |
| #include <string> |
| #include <utility> |
| |
| #include "base/component_export.h" |
| #include "base/containers/span.h" |
| #include "base/strings/string_piece.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| |
| namespace device { |
| namespace fido_filter { |
| |
| // This code is intended to allow Finch control of several cases that have |
| // cropped up over time: |
| // * Disabling a device on the USB bus that immediately answers all requests, |
| // thus stopping anything else from working. |
| // * Filtering attestation from devices that are found to be sending |
| // excessively identifying information. |
| // * Filtering attestation from websites that are performing tight |
| // allowlisting in a public context. |
| // * Filtering IDs from devices that are using them to tunnel other protocols. |
| // |
| // Thus Finch can set the "json" parameter of "WebAuthenticationFilter" to a |
| // JSON string with the following structure: |
| // |
| // { |
| // "filters": [{ |
| // "operation": matched against "ga" (getAssertion) or "mc" (makeCredential) |
| // "rp_id": matched against the RP ID (or AppID via the U2F API). Can be a |
| // list of such strings which matches if any element matches. |
| // "device": matched against the GetDisplayName value of the authenticator |
| // "id_type": matched against "cred" (credential IDs) or "user" (user IDs). |
| // "cred" matches against the allowCredentials of |
| // PublicKeyCredentialRequestOptions and the excludeCredentials |
| // of PublicKeyCredentialCreationOptions. "user" matches against |
| // the user.id of PublicKeyCredentialCreationOptions. |
| // "id": matched against the uppercase hex of the given ID. Can be |
| // a list of such strings which matches if any element matches. |
| // "id_min_size": matches if <= to the ID length, in bytes. |
| // "id_max_size": matches if >= to the ID length, in bytes. |
| // "action": "allow", "block", or "no-attestation". |
| // }, { ... } |
| // ]} |
| // |
| // The JSON is allowed to have trailing commas, unlike standard JSON. |
| // |
| // When strings are matched, it is using base/strings/pattern.h. Note the |
| // comment in that file well because it's more like a file glob than a regexp. |
| // |
| // The only required field is "action", but: |
| // * "id_type" must be given if "id_min_size", "id_max_size", or "id" are. |
| // * At least one of "device" or "rp_id" must be given. |
| // |
| // Any structural errors, or unknown keys, in the JSON cause a parse error and |
| // the filter fails open. |
| // |
| // A result of "block" rejects the action. If an action is blocked for all |
| // devices and doesn't specify an ID then it'll result in an immediate rejection |
| // of the WebAuthn Promise. Otherwise, a block causes an authenticator to be |
| // ignored, potentially hanging the request if all authenticators are ignored. A |
| // result of "allow" permits the action. (This can be useful to permit a narrow |
| // range of cases before blocking a wider range.) Lastly, a result of |
| // "no-attestation" causes attestation to be suppressed for makeCredential |
| // operations, unless the RP ID is listed in enterprise policy. |
| // |
| // At various points in the code, |Evaluate| can be called in order to process |
| // any configured filter. Before |Evaluate| can be called, |MaybeInitialize| |
| // must be called to check whether the filter has been updated. |
| // |
| // Processing stops at the first matching filter. If none match, |ALLOW| is |
| // returned. |
| |
| // MaybeInitialize parses any update to the Finch-controlled filter. |
| COMPONENT_EXPORT(DEVICE_FIDO) |
| void MaybeInitialize(); |
| |
| // Operation enumerates the possible operations for calling |Evaluate|. |
| enum class Operation { |
| MAKE_CREDENTIAL, |
| GET_ASSERTION, |
| }; |
| |
| // Operation enumerates the possible types of IDs for calling |Evaluate|. |
| enum class IDType { |
| CREDENTIAL_ID, |
| USER_ID, |
| }; |
| |
| // Action enumerates the result of evaluating a set of filterse. |
| enum class Action { |
| ALLOW = 1, |
| NO_ATTESTATION = 2, |
| BLOCK = 3, |
| }; |
| |
| // Evaluate consults any configured filters and returns the result of evaluating |
| // them. See above about the format of filters. |
| COMPONENT_EXPORT(DEVICE_FIDO) |
| Action Evaluate( |
| Operation op, |
| base::StringPiece rp_id, |
| absl::optional<base::StringPiece> device, |
| absl::optional<std::pair<IDType, base::span<const uint8_t>>> id); |
| |
| // ScopedFilterForTesting sets the current filter JSON for the duration of its |
| // lifetime. It is a fatal error if |json| is ill-formed. |
| class COMPONENT_EXPORT(DEVICE_FIDO) ScopedFilterForTesting { |
| public: |
| enum class PermitInvalidJSON { |
| kYes, |
| }; |
| |
| explicit ScopedFilterForTesting(base::StringPiece json); |
| ScopedFilterForTesting(base::StringPiece json, PermitInvalidJSON); |
| ~ScopedFilterForTesting(); |
| |
| private: |
| const absl::optional<std::string> previous_json_; |
| }; |
| |
| // ParseForTesting returns true iff |json| is a well-formed filter. |
| COMPONENT_EXPORT(DEVICE_FIDO) |
| bool ParseForTesting(base::StringPiece json); |
| |
| } // namespace fido_filter |
| } // namespace device |
| |
| #endif // DEVICE_FIDO_FILTER_H_ |