prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  TFA with Fido2 privacy intrution by requiring attestation
  **Describe the bug**
  Can't register TFA with a FIDO key without device attestation, making this feature unusable for privacy conscious developers.
  
  **Expected behavior**
  Respect users privacy, when registering FIDO keys by not requiring attestation. Most sites work without attestation pip should work without as well.
  
  **To Reproduce**
  - With any Yubikey supporting Fido2
  - With Firefox (92.0 (64-Bit)) go to https://pypi.org/manage/account/webauthn-provision
  - Try registering a Fido2 device
  - Wait for Firefox to bring up its privacy warning
  - Select "anonymize anyway" and continue
  - Pip says "Registration rejected. Error: Authenticator attestation is required.."
  
  ![Firefox fido2 pip](https://user-images.githubusercontent.com/82288872/134816676-9feb37a0-0519-46a8-a1d9-3acb068ca760.png)
  
  **My Platform**
  macOS 11.6
  Firefox 92.0 (64-Bit)
  Yubikey blue USB-A with NFC
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `warehouse/utils/webauthn.py`
  Content:
  ```
  1 # Licensed under the Apache License, Version 2.0 (the "License");
  2 # you may not use this file except in compliance with the License.
  3 # You may obtain a copy of the License at
  4 #
  5 # http://www.apache.org/licenses/LICENSE-2.0
  6 #
  7 # Unless required by applicable law or agreed to in writing, software
  8 # distributed under the License is distributed on an "AS IS" BASIS,
  9 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10 # See the License for the specific language governing permissions and
  11 # limitations under the License.
  12 
  13 import base64
  14 import os
  15 
  16 import webauthn as pywebauthn
  17 
  18 from webauthn.webauthn import (
  19     AuthenticationRejectedException as _AuthenticationRejectedException,
  20     RegistrationRejectedException as _RegistrationRejectedException,
  21 )
  22 
  23 
  24 class AuthenticationRejectedException(Exception):
  25     pass
  26 
  27 
  28 class RegistrationRejectedException(Exception):
  29     pass
  30 
  31 
  32 WebAuthnCredential = pywebauthn.WebAuthnCredential
  33 
  34 
  35 def _get_webauthn_users(user, *, rp_id):
  36     """
  37     Returns a webauthn.WebAuthnUser instance corresponding
  38     to the given user model, with properties suitable for
  39     usage within the webauthn API.
  40     """
  41     return [
  42         pywebauthn.WebAuthnUser(
  43             str(user.id),
  44             user.username,
  45             user.name or user.username,
  46             None,
  47             credential.credential_id,
  48             credential.public_key,
  49             credential.sign_count,
  50             rp_id,
  51         )
  52         for credential in user.webauthn
  53     ]
  54 
  55 
  56 def _webauthn_b64decode(encoded):
  57     padding = "=" * (len(encoded) % 4)
  58     return base64.urlsafe_b64decode(encoded + padding)
  59 
  60 
  61 def _webauthn_b64encode(source):
  62     return base64.urlsafe_b64encode(source).rstrip(b"=")
  63 
  64 
  65 def generate_webauthn_challenge():
  66     """
  67     Returns a random challenge suitable for use within
  68     Webauthn's credential and configuration option objects.
  69 
  70     See: https://w3c.github.io/webauthn/#cryptographic-challenges
  71     """
  72     # NOTE: Webauthn recommends at least 16 bytes of entropy,
  73     # we go with 32 because it doesn't cost us anything.
  74     return _webauthn_b64encode(os.urandom(32)).decode()
  75 
  76 
  77 def get_credential_options(user, *, challenge, rp_name, rp_id):
  78     """
  79     Returns a dictionary of options for credential creation
  80     on the client side.
  81     """
  82     options = pywebauthn.WebAuthnMakeCredentialOptions(
  83         challenge,
  84         rp_name,
  85         rp_id,
  86         str(user.id),
  87         user.username,
  88         user.name or user.username,
  89         None,
  90         user_verification="discouraged",
  91     )
  92 
  93     return options.registration_dict
  94 
  95 
  96 def get_assertion_options(user, *, challenge, rp_id):
  97     """
  98     Returns a dictionary of options for assertion retrieval
  99     on the client side.
  100     """
  101     options = pywebauthn.WebAuthnAssertionOptions(
  102         _get_webauthn_users(user, rp_id=rp_id), challenge
  103     )
  104 
  105     return options.assertion_dict
  106 
  107 
  108 def verify_registration_response(response, challenge, *, rp_id, origin):
  109     """
  110     Validates the challenge and attestation information
  111     sent from the client during device registration.
  112 
  113     Returns a WebAuthnCredential on success.
  114     Raises RegistrationRejectedException on failire.
  115     """
  116     # NOTE: We re-encode the challenge below, because our
  117     # response's clientData.challenge is encoded twice:
  118     # first for the entire clientData payload, and then again
  119     # for the individual challenge.
  120     encoded_challenge = _webauthn_b64encode(challenge.encode()).decode()
  121     response = pywebauthn.WebAuthnRegistrationResponse(
  122         rp_id, origin, response, encoded_challenge, self_attestation_permitted=True
  123     )
  124     try:
  125         return response.verify()
  126     except _RegistrationRejectedException as e:
  127         raise RegistrationRejectedException(str(e))
  128 
  129 
  130 def verify_assertion_response(assertion, *, challenge, user, origin, rp_id):
  131     """
  132     Validates the challenge and assertion information
  133     sent from the client during authentication.
  134 
  135     Returns an updated signage count on success.
  136     Raises AuthenticationRejectedException on failure.
  137     """
  138     webauthn_users = _get_webauthn_users(user, rp_id=rp_id)
  139     cred_ids = [cred.credential_id for cred in webauthn_users]
  140     encoded_challenge = _webauthn_b64encode(challenge.encode()).decode()
  141 
  142     for webauthn_user in webauthn_users:
  143         response = pywebauthn.WebAuthnAssertionResponse(
  144             webauthn_user,
  145             assertion,
  146             encoded_challenge,
  147             origin,
  148             allow_credentials=cred_ids,
  149         )
  150         try:
  151             return (webauthn_user.credential_id, response.verify())
  152         except _AuthenticationRejectedException:
  153             pass
  154 
  155     # If we exit the loop, then we've failed to verify the assertion against
  156     # any of the user's WebAuthn credentials. Fail.
  157     raise AuthenticationRejectedException("Invalid WebAuthn credential")
  158
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/warehouse/utils/webauthn.py b/warehouse/utils/webauthn.py
  --- a/warehouse/utils/webauthn.py
  +++ b/warehouse/utils/webauthn.py
  @@ -87,6 +87,7 @@
           user.username,
           user.name or user.username,
           None,
  +        attestation=None,
           user_verification="discouraged",
       )
   
  @@ -119,7 +120,12 @@
       # for the individual challenge.
       encoded_challenge = _webauthn_b64encode(challenge.encode()).decode()
       response = pywebauthn.WebAuthnRegistrationResponse(
  -        rp_id, origin, response, encoded_challenge, self_attestation_permitted=True
  +        rp_id,
  +        origin,
  +        response,
  +        encoded_challenge,
  +        self_attestation_permitted=True,
  +        none_attestation_permitted=True,
       )
       try:
           return response.verify()
  
verification_info: {"golden_diff": "diff --git a/warehouse/utils/webauthn.py b/warehouse/utils/webauthn.py\n--- a/warehouse/utils/webauthn.py\n+++ b/warehouse/utils/webauthn.py\n@@ -87,6 +87,7 @@\n         user.username,\n         user.name or user.username,\n         None,\n+        attestation=None,\n         user_verification=\"discouraged\",\n     )\n \n@@ -119,7 +120,12 @@\n     # for the individual challenge.\n     encoded_challenge = _webauthn_b64encode(challenge.encode()).decode()\n     response = pywebauthn.WebAuthnRegistrationResponse(\n-        rp_id, origin, response, encoded_challenge, self_attestation_permitted=True\n+        rp_id,\n+        origin,\n+        response,\n+        encoded_challenge,\n+        self_attestation_permitted=True,\n+        none_attestation_permitted=True,\n     )\n     try:\n         return response.verify()\n", "issue": "TFA with Fido2 privacy intrution by requiring attestation\n**Describe the bug**\r\nCan't register TFA with a FIDO key without device attestation, making this feature unusable for privacy conscious developers.\r\n\r\n**Expected behavior**\r\nRespect users privacy, when registering FIDO keys by not requiring attestation. Most sites work without attestation pip should work without as well.\r\n\r\n**To Reproduce**\r\n- With any Yubikey supporting Fido2\r\n- With Firefox (92.0 (64-Bit)) go to https://pypi.org/manage/account/webauthn-provision\r\n- Try registering a Fido2 device\r\n- Wait for Firefox to bring up its privacy warning\r\n- Select \"anonymize anyway\" and continue\r\n- Pip says \"Registration rejected. Error: Authenticator attestation is required..\"\r\n\r\n![Firefox fido2 pip](https://user-images.githubusercontent.com/82288872/134816676-9feb37a0-0519-46a8-a1d9-3acb068ca760.png)\r\n\r\n**My Platform**\r\nmacOS 11.6\r\nFirefox 92.0 (64-Bit)\r\nYubikey blue USB-A with NFC\r\n\n", "before_files": [{"content": "# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport base64\nimport os\n\nimport webauthn as pywebauthn\n\nfrom webauthn.webauthn import (\n    AuthenticationRejectedException as _AuthenticationRejectedException,\n    RegistrationRejectedException as _RegistrationRejectedException,\n)\n\n\nclass AuthenticationRejectedException(Exception):\n    pass\n\n\nclass RegistrationRejectedException(Exception):\n    pass\n\n\nWebAuthnCredential = pywebauthn.WebAuthnCredential\n\n\ndef _get_webauthn_users(user, *, rp_id):\n    \"\"\"\n    Returns a webauthn.WebAuthnUser instance corresponding\n    to the given user model, with properties suitable for\n    usage within the webauthn API.\n    \"\"\"\n    return [\n        pywebauthn.WebAuthnUser(\n            str(user.id),\n            user.username,\n            user.name or user.username,\n            None,\n            credential.credential_id,\n            credential.public_key,\n            credential.sign_count,\n            rp_id,\n        )\n        for credential in user.webauthn\n    ]\n\n\ndef _webauthn_b64decode(encoded):\n    padding = \"=\" * (len(encoded) % 4)\n    return base64.urlsafe_b64decode(encoded + padding)\n\n\ndef _webauthn_b64encode(source):\n    return base64.urlsafe_b64encode(source).rstrip(b\"=\")\n\n\ndef generate_webauthn_challenge():\n    \"\"\"\n    Returns a random challenge suitable for use within\n    Webauthn's credential and configuration option objects.\n\n    See: https://w3c.github.io/webauthn/#cryptographic-challenges\n    \"\"\"\n    # NOTE: Webauthn recommends at least 16 bytes of entropy,\n    # we go with 32 because it doesn't cost us anything.\n    return _webauthn_b64encode(os.urandom(32)).decode()\n\n\ndef get_credential_options(user, *, challenge, rp_name, rp_id):\n    \"\"\"\n    Returns a dictionary of options for credential creation\n    on the client side.\n    \"\"\"\n    options = pywebauthn.WebAuthnMakeCredentialOptions(\n        challenge,\n        rp_name,\n        rp_id,\n        str(user.id),\n        user.username,\n        user.name or user.username,\n        None,\n        user_verification=\"discouraged\",\n    )\n\n    return options.registration_dict\n\n\ndef get_assertion_options(user, *, challenge, rp_id):\n    \"\"\"\n    Returns a dictionary of options for assertion retrieval\n    on the client side.\n    \"\"\"\n    options = pywebauthn.WebAuthnAssertionOptions(\n        _get_webauthn_users(user, rp_id=rp_id), challenge\n    )\n\n    return options.assertion_dict\n\n\ndef verify_registration_response(response, challenge, *, rp_id, origin):\n    \"\"\"\n    Validates the challenge and attestation information\n    sent from the client during device registration.\n\n    Returns a WebAuthnCredential on success.\n    Raises RegistrationRejectedException on failire.\n    \"\"\"\n    # NOTE: We re-encode the challenge below, because our\n    # response's clientData.challenge is encoded twice:\n    # first for the entire clientData payload, and then again\n    # for the individual challenge.\n    encoded_challenge = _webauthn_b64encode(challenge.encode()).decode()\n    response = pywebauthn.WebAuthnRegistrationResponse(\n        rp_id, origin, response, encoded_challenge, self_attestation_permitted=True\n    )\n    try:\n        return response.verify()\n    except _RegistrationRejectedException as e:\n        raise RegistrationRejectedException(str(e))\n\n\ndef verify_assertion_response(assertion, *, challenge, user, origin, rp_id):\n    \"\"\"\n    Validates the challenge and assertion information\n    sent from the client during authentication.\n\n    Returns an updated signage count on success.\n    Raises AuthenticationRejectedException on failure.\n    \"\"\"\n    webauthn_users = _get_webauthn_users(user, rp_id=rp_id)\n    cred_ids = [cred.credential_id for cred in webauthn_users]\n    encoded_challenge = _webauthn_b64encode(challenge.encode()).decode()\n\n    for webauthn_user in webauthn_users:\n        response = pywebauthn.WebAuthnAssertionResponse(\n            webauthn_user,\n            assertion,\n            encoded_challenge,\n            origin,\n            allow_credentials=cred_ids,\n        )\n        try:\n            return (webauthn_user.credential_id, response.verify())\n        except _AuthenticationRejectedException:\n            pass\n\n    # If we exit the loop, then we've failed to verify the assertion against\n    # any of the user's WebAuthn credentials. Fail.\n    raise AuthenticationRejectedException(\"Invalid WebAuthn credential\")\n", "path": "warehouse/utils/webauthn.py"}], "after_files": [{"content": "# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport base64\nimport os\n\nimport webauthn as pywebauthn\n\nfrom webauthn.webauthn import (\n    AuthenticationRejectedException as _AuthenticationRejectedException,\n    RegistrationRejectedException as _RegistrationRejectedException,\n)\n\n\nclass AuthenticationRejectedException(Exception):\n    pass\n\n\nclass RegistrationRejectedException(Exception):\n    pass\n\n\nWebAuthnCredential = pywebauthn.WebAuthnCredential\n\n\ndef _get_webauthn_users(user, *, rp_id):\n    \"\"\"\n    Returns a webauthn.WebAuthnUser instance corresponding\n    to the given user model, with properties suitable for\n    usage within the webauthn API.\n    \"\"\"\n    return [\n        pywebauthn.WebAuthnUser(\n            str(user.id),\n            user.username,\n            user.name or user.username,\n            None,\n            credential.credential_id,\n            credential.public_key,\n            credential.sign_count,\n            rp_id,\n        )\n        for credential in user.webauthn\n    ]\n\n\ndef _webauthn_b64decode(encoded):\n    padding = \"=\" * (len(encoded) % 4)\n    return base64.urlsafe_b64decode(encoded + padding)\n\n\ndef _webauthn_b64encode(source):\n    return base64.urlsafe_b64encode(source).rstrip(b\"=\")\n\n\ndef generate_webauthn_challenge():\n    \"\"\"\n    Returns a random challenge suitable for use within\n    Webauthn's credential and configuration option objects.\n\n    See: https://w3c.github.io/webauthn/#cryptographic-challenges\n    \"\"\"\n    # NOTE: Webauthn recommends at least 16 bytes of entropy,\n    # we go with 32 because it doesn't cost us anything.\n    return _webauthn_b64encode(os.urandom(32)).decode()\n\n\ndef get_credential_options(user, *, challenge, rp_name, rp_id):\n    \"\"\"\n    Returns a dictionary of options for credential creation\n    on the client side.\n    \"\"\"\n    options = pywebauthn.WebAuthnMakeCredentialOptions(\n        challenge,\n        rp_name,\n        rp_id,\n        str(user.id),\n        user.username,\n        user.name or user.username,\n        None,\n        attestation=None,\n        user_verification=\"discouraged\",\n    )\n\n    return options.registration_dict\n\n\ndef get_assertion_options(user, *, challenge, rp_id):\n    \"\"\"\n    Returns a dictionary of options for assertion retrieval\n    on the client side.\n    \"\"\"\n    options = pywebauthn.WebAuthnAssertionOptions(\n        _get_webauthn_users(user, rp_id=rp_id), challenge\n    )\n\n    return options.assertion_dict\n\n\ndef verify_registration_response(response, challenge, *, rp_id, origin):\n    \"\"\"\n    Validates the challenge and attestation information\n    sent from the client during device registration.\n\n    Returns a WebAuthnCredential on success.\n    Raises RegistrationRejectedException on failire.\n    \"\"\"\n    # NOTE: We re-encode the challenge below, because our\n    # response's clientData.challenge is encoded twice:\n    # first for the entire clientData payload, and then again\n    # for the individual challenge.\n    encoded_challenge = _webauthn_b64encode(challenge.encode()).decode()\n    response = pywebauthn.WebAuthnRegistrationResponse(\n        rp_id,\n        origin,\n        response,\n        encoded_challenge,\n        self_attestation_permitted=True,\n        none_attestation_permitted=True,\n    )\n    try:\n        return response.verify()\n    except _RegistrationRejectedException as e:\n        raise RegistrationRejectedException(str(e))\n\n\ndef verify_assertion_response(assertion, *, challenge, user, origin, rp_id):\n    \"\"\"\n    Validates the challenge and assertion information\n    sent from the client during authentication.\n\n    Returns an updated signage count on success.\n    Raises AuthenticationRejectedException on failure.\n    \"\"\"\n    webauthn_users = _get_webauthn_users(user, rp_id=rp_id)\n    cred_ids = [cred.credential_id for cred in webauthn_users]\n    encoded_challenge = _webauthn_b64encode(challenge.encode()).decode()\n\n    for webauthn_user in webauthn_users:\n        response = pywebauthn.WebAuthnAssertionResponse(\n            webauthn_user,\n            assertion,\n            encoded_challenge,\n            origin,\n            allow_credentials=cred_ids,\n        )\n        try:\n            return (webauthn_user.credential_id, response.verify())\n        except _AuthenticationRejectedException:\n            pass\n\n    # If we exit the loop, then we've failed to verify the assertion against\n    # any of the user's WebAuthn credentials. Fail.\n    raise AuthenticationRejectedException(\"Invalid WebAuthn credential\")\n", "path": "warehouse/utils/webauthn.py"}]}