Repository: duo-labs/py_webauthn

Issue:
Tweak SafetyNet check
SafetyNet-based attestation is on its way out for Android Keystore attestation via Play Integrity (if I'm understanding https://developer.android.com/privacy-and-security/safetynet/deprecation-timeline and https://android-developers.googleblog.com/2024/09/attestation-format-change-for-android-fido2-api.html correctly.) In this transition state it's possible for a device returning `"android-safetynet"` attestation to return responses with `"basicIntegrity": True` but `"ctsProfileMatch": False`.

Documentation on SafetyNet attestation verification is insufficient in the spec as the link it points to goes to that SafetyNet deprecation timeline. One proposed solution to make py_webauthn more flexible for use with passkeys is to change the requirement that `ctsProfileMatch` be true...

https://github.com/duo-labs/py_webauthn/blob/42537f58bb2dc073c03e7fd0f9dbf10d8c3b2c77/webauthn/registration/formats/android_safetynet.py#L142-L143

...to only require that `basicIntegrity` to be true. Adding a flag for this is probably a good idea, similar to what happened over here:

https://github.com/lbuchs/WebAuthn/commit/deb37c7314b5cf16cd4c36707339495f326be63c

Discussion:

Solution patch:
diff --git a/webauthn/helpers/known_root_certs.py b/webauthn/helpers/known_root_certs.py
index 6d93e43..27be851 100644
--- a/webauthn/helpers/known_root_certs.py
+++ b/webauthn/helpers/known_root_certs.py
@@ -93,6 +93,100 @@ ex0SdDrx+tWUDqG8At2JHA==
     "ascii"
 )
 
+#######################################
+#
+# Google Hardware Attestation Root 3
+#
+# Downloaded from https://developer.android.com/training/articles/security-key-attestation#root_certificate
+# (third entry)
+#
+# Valid until 2036-11-13 @ 15:10 PST
+#
+# SHA256 Fingerprint
+# AB:66:41:17:8A:36:E1:79:AA:0C:1C:DD:DF:9A:16:EB:45:FA:20:94:3E:2B:8C:D7:C7:C0:5C:26:CF:8B:48:7A
+#
+#######################################
+google_hardware_attestation_root_3 = """-----BEGIN CERTIFICATE-----
+MIIFHDCCAwSgAwIBAgIJAMNrfES5rhgxMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV
+BAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMjExMTE3MjMxMDQyWhcNMzYxMTEzMjMx
+MDQyWjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdS
+Sxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7
+tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggj
+nar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGq
+C4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQ
+oVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+O
+JtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/Eg
+sTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRi
+igHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+M
+RPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9E
+aDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5Um
+AGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1Ud
+IwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYD
+VR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQBTNNZe5cuf8oiq+jV0itTG
+zWVhSTjOBEk2FQvh11J3o3lna0o7rd8RFHnN00q4hi6TapFhh4qaw/iG6Xg+xOan
+63niLWIC5GOPFgPeYXM9+nBb3zZzC8ABypYuCusWCmt6Tn3+Pjbz3MTVhRGXuT/T
+QH4KGFY4PhvzAyXwdjTOCXID+aHud4RLcSySr0Fq/L+R8TWalvM1wJJPhyRjqRCJ
+erGtfBagiALzvhnmY7U1qFcS0NCnKjoO7oFedKdWlZz0YAfu3aGCJd4KHT0MsGiL
+Zez9WP81xYSrKMNEsDK+zK5fVzw6jA7cxmpXcARTnmAuGUeI7VVDhDzKeVOctf3a
+0qQLwC+d0+xrETZ4r2fRGNw2YEs2W8Qj6oDcfPvq9JySe7pJ6wcHnl5EZ0lwc4xH
+7Y4Dx9RA1JlfooLMw3tOdJZH0enxPXaydfAD3YifeZpFaUzicHeLzVJLt9dvGB0b
+HQLE4+EqKFgOZv2EoP686DQqbVS1u+9k0p2xbMA105TBIk7npraa8VM0fnrRKi7w
+lZKwdH+aNAyhbXRW9xsnODJ+g8eF452zvbiKKngEKirK5LGieoXBX7tZ9D1GNBH2
+Ob3bKOwwIWdEFle/YF/h6zWgdeoaNGDqVBrLr2+0DtWoiB1aDEjLWl9FmyIUyUm7
+mD/vFDkzF+wm7cyWpQpCVQ==
+-----END CERTIFICATE-----
+""".encode(
+    "ascii"
+)
+
+#######################################
+#
+# Google Hardware Attestation Root 4
+#
+# Downloaded from https://developer.android.com/training/articles/security-key-attestation#root_certificate
+# (fourth entry)
+#
+# Valid until 2042-03-15 @ 11:07 PDT
+#
+# SHA256 Fingerprint
+# CE:DB:1C:B6:DC:89:6A:E5:EC:79:73:48:BC:E9:28:67:53:C2:B3:8E:E7:1C:E0:FB:E3:4A:9A:12:48:80:0D:FC
+#
+#######################################
+google_hardware_attestation_root_4 = """-----BEGIN CERTIFICATE-----
+MIIFHDCCAwSgAwIBAgIJAPHBcqaZ6vUdMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV
+BAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMjIwMzIwMTgwNzQ4WhcNNDIwMzE1MTgw
+NzQ4WjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdS
+Sxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7
+tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggj
+nar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGq
+C4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQ
+oVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+O
+JtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/Eg
+sTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRi
+igHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+M
+RPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9E
+aDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5Um
+AGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1Ud
+IwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYD
+VR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQB8cMqTllHc8U+qCrOlg3H7
+174lmaCsbo/bJ0C17JEgMLb4kvrqsXZs01U3mB/qABg/1t5Pd5AORHARs1hhqGIC
+W/nKMav574f9rZN4PC2ZlufGXb7sIdJpGiO9ctRhiLuYuly10JccUZGEHpHSYM2G
+tkgYbZba6lsCPYAAP83cyDV+1aOkTf1RCp/lM0PKvmxYN10RYsK631jrleGdcdkx
+oSK//mSQbgcWnmAEZrzHoF1/0gso1HZgIn0YLzVhLSA/iXCX4QT2h3J5z3znluKG
+1nv8NQdxei2DIIhASWfu804CA96cQKTTlaae2fweqXjdN1/v2nqOhngNyz1361mF
+mr4XmaKH/ItTwOe72NI9ZcwS1lVaCvsIkTDCEXdm9rCNPAY10iTunIHFXRh+7KPz
+lHGewCq/8TOohBRn0/NNfh7uRslOSZ/xKbN9tMBtw37Z8d2vvnXq/YWdsm1+JLVw
+n6yYD/yacNJBlwpddla8eaVMjsF6nBnIgQOf9zKSe06nSTqvgwUHosgOECZJZ1Eu
+zbH4yswbt02tKtKEFhx+v+OTge/06V+jGsqTWLsfrOCNLuA8H++z+pUENmpqnnHo
+vaI47gC+TNpkgYGkkBT6B/m/U01BuOBBTzhIlMEZq9qkDWuM2cA5kW5V3FJUcfHn
+w1IdYIg2Wxg7yHcQZemFQg==
+-----END CERTIFICATE-----
+""".encode(
+    "ascii"
+)
+
 #######################################
 #
 # GlobalSign Root CA
diff --git a/webauthn/registration/formats/android_key.py b/webauthn/registration/formats/android_key.py
index a9c9c6b..98840c8 100644
--- a/webauthn/registration/formats/android_key.py
+++ b/webauthn/registration/formats/android_key.py
@@ -1,6 +1,7 @@
 import hashlib
 from typing import List
 
+from asn1crypto.core import OctetString
 from cryptography import x509
 from cryptography.exceptions import InvalidSignature
 from cryptography.hazmat.backends import default_backend
@@ -32,6 +33,8 @@ from webauthn.helpers.exceptions import (
 from webauthn.helpers.known_root_certs import (
     google_hardware_attestation_root_1,
     google_hardware_attestation_root_2,
+    google_hardware_attestation_root_3,
+    google_hardware_attestation_root_4,
 )
 from webauthn.helpers.structs import AttestationStatement
 
@@ -48,7 +51,7 @@ def verify_android_key(
 
     See https://www.w3.org/TR/webauthn-2/#sctn-android-key-attestation
 
-    Also referenced: https://source.android.com/security/keystore/attestation
+    Also referenced: https://source.android.com/docs/security/features/keystore/attestation
     """
     if not attestation_statement.sig:
         raise InvalidRegistrationResponse(
@@ -63,19 +66,33 @@ def verify_android_key(
     if not attestation_statement.x5c:
         raise InvalidRegistrationResponse("Attestation statement was missing x5c (Android Key)")
 
-    # Validate certificate chain
-    try:
-        # Include known root certificates for this attestation format
-        pem_root_certs_bytes.append(google_hardware_attestation_root_1)
-        pem_root_certs_bytes.append(google_hardware_attestation_root_2)
+    # x5c includes a root certificate, so break it up accordingly
+    x5c_no_root = attestation_statement.x5c[:-1]
+    x5c_root_cert = attestation_statement.x5c[-1]
+
+    x5c_root_cert_x509 = x509.load_der_x509_certificate(x5c_root_cert, default_backend())
+    x5c_root_cert_pem = x5c_root_cert_x509.public_bytes(Encoding.PEM)
 
+    # Make sure x509 forms a complete, valid cert chain
+    try:
         validate_certificate_chain(
-            x5c=attestation_statement.x5c,
-            pem_root_certs_bytes=pem_root_certs_bytes,
+            x5c=x5c_no_root,
+            pem_root_certs_bytes=[x5c_root_cert_pem],
         )
     except InvalidCertificateChain as err:
         raise InvalidRegistrationResponse(f"{err} (Android Key)")
 
+    # Make sure the root cert is one of these
+    pem_root_certs_bytes.append(google_hardware_attestation_root_1)
+    pem_root_certs_bytes.append(google_hardware_attestation_root_2)
+    pem_root_certs_bytes.append(google_hardware_attestation_root_3)
+    pem_root_certs_bytes.append(google_hardware_attestation_root_4)
+
+    if x5c_root_cert_pem not in pem_root_certs_bytes:
+        raise InvalidRegistrationResponse(
+            "x5c root certificate was not a known root certificate (Android Key)"
+        )
+
     # Extract attStmt bytes from attestation_object
     attestation_dict = parse_cbor(attestation_object)
     authenticator_data_bytes = attestation_dict["authData"]
@@ -150,6 +167,14 @@ def verify_android_key(
     ext_value: bytes = ext_value_wrapper.value
     parsed_ext = KeyDescription.load(ext_value)
 
+    # Verify that the attestationChallenge field in the attestation certificate extension data
+    # is identical to clientDataHash.
+    attestation_challenge: OctetString = parsed_ext["attestationChallenge"]
+    if bytes(attestation_challenge) != client_data_hash_bytes:
+        raise InvalidRegistrationResponse(
+            "attestationChallenge field was not the same as the hash of clientDataJSON (Android Key)"
+        )
+
     # Verify the following using the appropriate authorization list from the attestation
     # certificate extension data:
     software_enforced: AuthorizationList = parsed_ext["softwareEnforced"]
diff --git a/webauthn/registration/formats/android_safetynet.py b/webauthn/registration/formats/android_safetynet.py
index cef6bdf..b37ff31 100644
--- a/webauthn/registration/formats/android_safetynet.py
+++ b/webauthn/registration/formats/android_safetynet.py
@@ -139,7 +139,7 @@ def verify_android_safetynet(
     # by following the steps in the SafetyNet online documentation.
     x5c = [base64url_to_bytes(cert) for cert in header.x5c]
 
-    if not payload.cts_profile_match:
+    if not payload.basic_integrity:
         raise InvalidRegistrationResponse("Could not verify device integrity (SafetyNet)")
 
     if verify_timestamp_ms:


Test patch:
diff --git a/tests/test_verify_registration_response_android_key.py b/tests/test_verify_registration_response_android_key.py
index 4608d38..9c08a55 100644
--- a/tests/test_verify_registration_response_android_key.py
+++ b/tests/test_verify_registration_response_android_key.py
@@ -1,5 +1,4 @@
 from unittest import TestCase
-from unittest.mock import MagicMock, patch
 
 from webauthn.helpers import base64url_to_bytes
 from webauthn.helpers.structs import AttestationFormat
@@ -7,28 +6,32 @@ from webauthn import verify_registration_response
 
 
 class TestVerifyRegistrationResponseAndroidKey(TestCase):
-    @patch("OpenSSL.crypto.X509StoreContext.verify_certificate")
-    def test_verify_attestation_android_key(
-        self,
-        mock_verify_certificate: MagicMock,
-    ) -> None:
-        # Mocked because response is from FIDO Conformance with unknown root certs
-        mock_verify_certificate.return_value = True
+    def test_verify_attestation_android_key_hardware_authority(self):
+        """
+        This android-key attestation was generated on a Pixel 8a in January 2025 via an origin
+        trial. Google will be sunsetting android-safetynet attestation for android-key attestations
+        for device-bound passkeys (i.e. `"residentKey": "discouraged"`) in April 2025
 
+        See here for more info:
+        https://android-developers.googleblog.com/2024/09/attestation-format-change-for-android-fido2-api.html
+        """
         credential = """{
-            "id": "V51GE29tGbhby7sbg1cZ_qL8V8njqEsXpAnwQBobvgw",
-            "rawId": "V51GE29tGbhby7sbg1cZ_qL8V8njqEsXpAnwQBobvgw",
+            "id": "AYNe4CBKc8H30FuAb8uaht6JbEQfbSBnS0SX7B6MFg8ofI92oR5lheRDJCgwY-JqB_QSJtezdhMbf8Wzt_La5N0",
+            "rawId": "AYNe4CBKc8H30FuAb8uaht6JbEQfbSBnS0SX7B6MFg8ofI92oR5lheRDJCgwY-JqB_QSJtezdhMbf8Wzt_La5N0",
             "response": {
-                "attestationObject": "o2NmbXRrYW5kcm9pZC1rZXlnYXR0U3RtdKNjYWxnJmNzaWdYRzBFAiAbZhfcF0KSXj5rdEevvnBcC8ZfRQlNl9XYWRTiIGKSHwIhAIerc7jWjOF_lJ71n_GAcaHwDUtPxkjAAdYugnZ4QxkmY3g1Y4JZAxowggMWMIICvaADAgECAgEBMAoGCCqGSM49BAMCMIHkMUUwQwYDVQQDDDxGQUtFIEFuZHJvaWQgS2V5c3RvcmUgU29mdHdhcmUgQXR0ZXN0YXRpb24gSW50ZXJtZWRpYXRlIEZBS0UxMTAvBgkqhkiG9w0BCQEWImNvbmZvcm1hbmNlLXRvb2xzQGZpZG9hbGxpYW5jZS5vcmcxFjAUBgNVBAoMDUZJRE8gQWxsaWFuY2UxIjAgBgNVBAsMGUF1dGhlbnRpY2F0b3IgQXR0ZXN0YXRpb24xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNWTESMBAGA1UEBwwJV2FrZWZpZWxkMCAXDTcwMDIwMTAwMDAwMFoYDzIwOTkwMTMxMjM1OTU5WjApMScwJQYDVQQDDB5GQUtFIEFuZHJvaWQgS2V5c3RvcmUgS2V5IEZBS0UwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARuowgSu5AoRj8Vi_ZNSFBbGUZJXFG9MkDT6jADlr7tOK9NEgjVX53-ergXpyPaFZrAR9py-xnzfjILn_Kzb8Iqo4IBFjCCARIwCwYDVR0PBAQDAgeAMIHhBgorBgEEAdZ5AgERBIHSMIHPAgECCgEAAgEBCgEABCCfVEl83pSDSerk9I3pcICNTdzc5N3u4jt21cXdzBuJjgQAMGm_hT0IAgYBXtPjz6C_hUVZBFcwVTEvMC0EKGNvbS5hbmRyb2lkLmtleXN0b3JlLmFuZHJvaWRrZXlzdG9yZWRlbW8CAQExIgQgdM_LUHSI9SkQhZHHpQWRnzJ3MvvB2ANSauqYAAbS2JgwMqEFMQMCAQKiAwIBA6MEAgIBAKUFMQMCAQSqAwIBAb-DeAMCAQK_hT4DAgEAv4U_AgUAMB8GA1UdIwQYMBaAFKPSqizvDYzyJALVHLRgvL9qWyQUMAoGCCqGSM49BAMCA0cAMEQCIC7WHb2PyULnjp1M1TVI3Wti_eDhe6sFweuQAdecXtHhAiAS_eZkFsx_VNsrTu3XfZ2D7wIt-vT6nTljfHZ4zqU5xlkDGDCCAxQwggK6oAMCAQICAQIwCgYIKoZIzj0EAwIwgdwxPTA7BgNVBAMMNEZBS0UgQW5kcm9pZCBLZXlzdG9yZSBTb2Z0d2FyZSBBdHRlc3RhdGlvbiBSb290IEZBS0UxMTAvBgkqhkiG9w0BCQEWImNvbmZvcm1hbmNlLXRvb2xzQGZpZG9hbGxpYW5jZS5vcmcxFjAUBgNVBAoMDUZJRE8gQWxsaWFuY2UxIjAgBgNVBAsMGUF1dGhlbnRpY2F0b3IgQXR0ZXN0YXRpb24xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNWTESMBAGA1UEBwwJV2FrZWZpZWxkMB4XDTE5MDQyNTA1NDkzMloXDTQ2MDkxMDA1NDkzMlowgeQxRTBDBgNVBAMMPEZBS0UgQW5kcm9pZCBLZXlzdG9yZSBTb2Z0d2FyZSBBdHRlc3RhdGlvbiBJbnRlcm1lZGlhdGUgRkFLRTExMC8GCSqGSIb3DQEJARYiY29uZm9ybWFuY2UtdG9vbHNAZmlkb2FsbGlhbmNlLm9yZzEWMBQGA1UECgwNRklETyBBbGxpYW5jZTEiMCAGA1UECwwZQXV0aGVudGljYXRvciBBdHRlc3RhdGlvbjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1ZMRIwEAYDVQQHDAlXYWtlZmllbGQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASrUGErYk0Xu8O1GwRJOwVJC4wfi52883my3tygfFKh17YN0yF13Ct-3bwm2wjVX4b2cbaU3DBNpKKKjE4DpvXHo2MwYTAPBgNVHRMBAf8EBTADAQH_MA4GA1UdDwEB_wQEAwIChDAdBgNVHQ4EFgQUo9KqLO8NjPIkAtUctGC8v2pbJBQwHwYDVR0jBBgwFoAUUpobMuBWqs1RD-9fgDcGi_KRIx0wCgYIKoZIzj0EAwIDSAAwRQIhALFvLkAvtHrObTmN8P0-yLIT496P_weSEEbB6vCJWSh9AiBu-UOorCeLcF4WixOG9E5Li2nXe4uM2q6mbKGkll8u-WhhdXRoRGF0YVikPdxHEOnAiLIp26idVjIguzn3Ipr_RlsKZWsa-5qK-KBBAAAAYFUOS1SqR0CfmpUat2wTATEAIFedRhNvbRm4W8u7G4NXGf6i_FfJ46hLF6QJ8EAaG74MpQECAyYgASFYIG6jCBK7kChGPxWL9k1IUFsZRklcUb0yQNPqMAOWvu04Ilggr00SCNVfnf56uBenI9oVmsBH2nL7GfN-Mguf8rNvwio",
-                "clientDataJSON": "eyJvcmlnaW4iOiJodHRwczovL2Rldi5kb250bmVlZGEucHciLCJjaGFsbGVuZ2UiOiI0YWI3ZGZkMS1hNjk1LTQ3NzctOTg1Zi1hZDI5OTM4MjhlOTkiLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0"
+                "attestationObject": "o2NmbXRrYW5kcm9pZC1rZXlnYXR0U3RtdKNjYWxnJmNzaWdYSDBGAiEAs9Aufj5f5HyLKEFsgfmqyaXfAih-hGuTJqgmxZGijzYCIQDAMddAq1gwH3MtesYR6WE6IAockRz8ilR7CFw_kgdmv2N4NWOFWQLQMIICzDCCAnKgAwIBAgIBATAKBggqhkjOPQQDAjA5MSkwJwYDVQQDEyBkNjAyYTAzYTY3MmQ4NjViYTVhNDg1ZTMzYTIwN2M3MzEMMAoGA1UEChMDVEVFMB4XDTcwMDEwMTAwMDAwMFoXDTQ4MDEwMTAwMDAwMFowHzEdMBsGA1UEAxMUQW5kcm9pZCBLZXlzdG9yZSBLZXkwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATXVi3-n-rBsrP3A4Pj9P8e6PNh3eNdC38PaFiCZyMWdUVA6PbE6985PSUDDcnk3Knnpyc66J_HFOu_geuqiWtAo4IBgzCCAX8wDgYDVR0PAQH_BAQDAgeAMIIBawYKKwYBBAHWeQIBEQSCAVswggFXAgIBLAoBAQICASwKAQEEIFZS4txFVJqW-Wr6IlUC-H-twIpgvAITksC-jFBi_V9eBAAwd7-FPQgCBgGUcHc4or-FRWcEZTBjMT0wGwQWY29tLmdvb2dsZS5hbmRyb2lkLmdzZgIBIzAeBBZjb20uZ29vZ2xlLmFuZHJvaWQuZ21zAgQO6jzjMSIEIPD9bFtBDyXLJcO1M0bIly-uMPjudBHfkQSArWstYNuDMIGpoQUxAwIBAqIDAgEDowQCAgEApQUxAwIBBKoDAgEBv4N4AwIBA7-DeQMCAQq_hT4DAgEAv4VATDBKBCCd4l-wK7VTDUQUnRSEN8guJn5VcyJTCqbwOwrC6Skx2gEB_woBAAQg6y0px0ZXc5v2bsVb45w-6IiMbXzp3gyHIWKS1mbz6gu_hUEFAgMCSfC_hUIFAgMDFwW_hU4GAgQBNP35v4VPBgIEATT9-TAKBggqhkjOPQQDAgNIADBFAiEAzNz6wyTo4t5ixo9G4zXPwh4zSB9F854sU_KDGTf0dxYCICaQVSWzWgTZLQYv13MXJJee8S8_luQB3W5lPPzP0exsWQHjMIIB3zCCAYWgAwIBAgIRANYCoDpnLYZbpaSF4zogfHMwCgYIKoZIzj0EAwIwKTETMBEGA1UEChMKR29vZ2xlIExMQzESMBAGA1UEAxMJRHJvaWQgQ0EzMB4XDTI1MDEwNzE3MDg0M1oXDTI1MDIwMjEwMzUyN1owOTEpMCcGA1UEAxMgZDYwMmEwM2E2NzJkODY1YmE1YTQ4NWUzM2EyMDdjNzMxDDAKBgNVBAoTA1RFRTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFPbPYqm91rYvZVCBdFaHRMg0tw7U07JA1EcD9ZP4d0lK2NFM4A0wGKS4jbTR_bu7NTt_YyF388S0PWAJTluqnOjfjB8MB0GA1UdDgQWBBSXyrsZ_A1NnJGRq0sm2G9nm-NC5zAfBgNVHSMEGDAWgBTFUX4F2MtjWykYrAIa8sh9bBL-kjAPBgNVHRMBAf8EBTADAQH_MA4GA1UdDwEB_wQEAwICBDAZBgorBgEEAdZ5AgEeBAuiAQgDZkdvb2dsZTAKBggqhkjOPQQDAgNIADBFAiEAysd6JDoI8X4NEdrRwUwtIAy-hLxSEKUVS2XVWS2CP04CIFNQQzM4TkA_xaZj8KyiS61nb-aOBP35tlA34JCOlv9nWQHcMIIB2DCCAV2gAwIBAgIUAIUK9vrO5iIEbQx0izdwqlWwtk0wCgYIKoZIzj0EAwMwKTETMBEGA1UEChMKR29vZ2xlIExMQzESMBAGA1UEAxMJRHJvaWQgQ0EyMB4XDTI0MTIwOTA2Mjg1M1oXDTI1MDIxNzA2Mjg1MlowKTETMBEGA1UEChMKR29vZ2xlIExMQzESMBAGA1UEAxMJRHJvaWQgQ0EzMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPjbr-yt9xhgcbKLXoN3RK-1FcCjwIpeMPJZjayW0dqNtFflHp2smO0DxN_6x7M7NAGbcC9lM1_E-N6z51ODv-6NjMGEwDgYDVR0PAQH_BAQDAgIEMA8GA1UdEwEB_wQFMAMBAf8wHQYDVR0OBBYEFMVRfgXYy2NbKRisAhryyH1sEv6SMB8GA1UdIwQYMBaAFKYLhqTwyH8ztWE5Ys0956c6QoNIMAoGCCqGSM49BAMDA2kAMGYCMQCuzU0wV_NkOQzgqzyqP66SJN6lilrU-NDVU6qNCnbFsUoZQOm4wBwUw7LqfoUhx7YCMQDFEvqHfc2hwN2J4I9Z4rTHiLlsy6gA33WvECzIZmVMpKcyEiHlm4c9XR0nVkAjQ_5ZA4QwggOAMIIBaKADAgECAgoDiCZnYGWJloYOMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMjIwMTI2MjI0OTQ1WhcNMzcwMTIyMjI0OTQ1WjApMRMwEQYDVQQKEwpHb29nbGUgTExDMRIwEAYDVQQDEwlEcm9pZCBDQTIwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT72ZtYJ0I2etFhouvtVs0sBzvYsx8thNCZV1wsDPvsMDSTPij-M1wBFD00OUn2bfU5b7K2_t2NkXc2-_V9g--mdb6SoRGmJ_AG9ScY60LKSA7iPT7gZ_5-q0tnEPPZJCqjZjBkMB0GA1UdDgQWBBSmC4ak8Mh_M7VhOWLNPeenOkKDSDAfBgNVHSMEGDAWgBQ2YeEAfIgFCVGLRGxH_xpMyepPEjASBgNVHRMBAf8ECDAGAQH_AgECMA4GA1UdDwEB_wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEArpB2eLbKHNcS6Q3Td3N7ZCgVLN0qA7CboM-Ftu4YYAcHxh-e_sk7T7XOg5S4d9a_DD7mIXgENSBPB_fVqCnBaSDKNJ3nUuC1_9gcT95p4kKJo0tqcsWw8WgKVJhNuZCN7d_ziHLiRRcrKtaj944THzsy7vB-pSai7gTah_RJrDQI91bDUJgld8_p_QAbVnYA8o-msO0sRKxgF1V5QuBwBTfpdkqshqL3nwBm0sofqI_rM-JOQava3-IurHvfkzioiOJ0uFJnBGVjpZFwGwsmyKwzl-3qRKlkHggAOKt3lQQ4GiJnOCm10JrxPa2Za0K6_kyk6YyvvRcFNai5ej3nMKJPg-eeG2nST6N6ePFuaeoNQnD4XkagGFEQYzcqvsdFsmsbUFMghFl7zEVYdscuSgCG939wxW1JgKyG5ce7CI40328w9IuOf8mUS_W3i4jSfxqCJbegyo_SKDpDILnhJUBy0T3fN8mv9AyO0uoJBlvnogIVv2SdpYUt92vyOiGMy3Jx_ZRWjIRa7iIV3VnjLI__pgCrXQLMinZWEWsxVxg25nrk8u32nZd67DJN3k2FufRbsmHZly9CLo0P79lkIEC3rifLqqJeDyHQNaBMUC6BSDZ5RJCtMjSZw2xL5z0X9_zBsKVPkMW61hMhKzVmYNLe1DJQANRP-enru5i1oXlZBSAwggUcMIIDBKADAgECAgkA1Q_yW6Py1rMwDQYJKoZIhvcNAQELBQAwGzEZMBcGA1UEBRMQZjkyMDA5ZTg1M2I2YjA0NTAeFw0xOTExMjIyMDM3NThaFw0zNDExMTgyMDM3NThaMBsxGTAXBgNVBAUTEGY5MjAwOWU4NTNiNmIwNDUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCvtseCK7GnAewrtC6LzFQWY6vvmC8yx391MQMMl1JLG1_oCfvHKqlFH3Q8vZpvEzV0SqVed_a2rDU17hfCXmOVF92ckuY3SlPL_iWPj_u2_RKTeKIqTKmcRS1HpZ8yAfRBl8oczX52L7L1MVG2_rL__Stv5P5bxr2ew0v-CCOdqvzrjrWo7Ss6zZxeOneQ4bUUQnkxWYWYEa2esqlrvdelfJOpHEH8zSfWf9b2caoLgVJhrThPo3lEhkYE3bPYxPkgoZsWVsLxStbQPFbsBgiZBBwe0aX-bTRAtVa60dChUlicU-VdNwdi8BIu75GGGxsObEyAknSZwOm-wLg-O8H5PHLASWBLvS8TReYsP44m2-wGyUdm88EoI51PQxL62BI4h-Br7PVnWDv4NVqB_uq6-ZqDyN8-KjIq_Gcr8SCxNRWLaCHOrzCbbu53-YgzsBjaoQ5FHwajdNUHgfNZCClmu3eLkwiUJpjnTgvNJGKKAcLMA-UfCz5bSsHk356vn_akkqd8FIOIKIUBW0Is5nuAuIybSOE7YHq1Rccj_4xE-PLTaLn2Ug0xFF6_noYq1x32o7_SRQlZ1lN0DZehLzaLE-9m1dClSm4vXZpv70RoMrxnhEclhh8JPdDm80BdqJZD7w9NabZCAFH9uTBJZz42lQWA0830-9CLxYSDlSYAYwIDAQABo2MwYTAdBgNVHQ4EFgQUNmHhAHyIBQlRi0RsR_8aTMnqTxIwHwYDVR0jBBgwFoAUNmHhAHyIBQlRi0RsR_8aTMnqTxIwDwYDVR0TAQH_BAUwAwEB_zAOBgNVHQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADggIBAE4xoFzyi6Zdva-hztcJae5cqEEErd7YowbPf23uUDdddF7ZkssCQsznLcnu1RGR_lrVK61907JcCZ4TpJGjzdSHpazOh2YyTErkYzgkaue3ikGKy7mKBcTJ1pbuqrYJ0LoM4aMb6YSQ3z9MDqndyegv-w_LPp692MuVJ4nysUEfrFbIhkJutylgQnNdpQ4RrHFfGBjPn9xOJUo3YzUbaiRAFQhhJjpuMQvhpQ3lx-juiA_dS-WISjcSjRiDC7NHa_QpHoLVxmpklJOeCEgL-8APfYp01D5zc36-XY5OxRUwLUaJaSeA3HU47X6Rdb5hOedNQ604izBQ_9Wp3lJiAAiYwB9jxT3-IiCRCPpPZboWxJzL3gg318WETVS3OYugEi5QWxVckxPP4m5y2H4iqhYW5r2_VH3f-T3ynjWmO0Vf4fwOyVWB8_T3u-O7goOWo3rjFXWCvDdkuXgKI578D3Wh4ubZQc6rrCfd6wHivYQhApvqNNUa7mxgJx1alevQBRWpwAE92Av4fuomC4HDT2iObrE0ivDY6hysMqy52T-iSv8DCoTI8rD1acyVCAsgrDWs4MbY29T2hHcZUZ0yRQFm60vxW4WQRFAa3q9DY4LDSxXjtUyS5htpwr_HJkWJFys8k9vjXOBtCP1cATIsoId7HRJ0OvH61ZQOobwC3YkcaGF1dGhEYXRhWMVJlg3liA6MaHQ0Fw9kdmBbj-SuuaKGMseZXPO6gx2XY0UAAAAAuT_ZYfLmRi-xIoIAIkfeeABBAYNe4CBKc8H30FuAb8uaht6JbEQfbSBnS0SX7B6MFg8ofI92oR5lheRDJCgwY-JqB_QSJtezdhMbf8Wzt_La5N2lAQIDJiABIVgg11Yt_p_qwbKz9wOD4_T_HujzYd3jXQt_D2hYgmcjFnUiWCBFQOj2xOvfOT0lAw3J5Nyp56cnOuifxxTrv4HrqolrQA",
+                "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoidDRMV0kwaVlKU1RXUGw5V1hVZE5oZEhBbnJQRExGOWVXQVA5bEhnbUhQOCIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODAwMCIsImNyb3NzT3JpZ2luIjpmYWxzZX0"
             },
             "type": "public-key",
-            "clientExtensionResults": {}
+            "clientExtensionResults": {
+                "credProps": { "rk": false }
+            },
+            "authenticatorAttachment": "platform"
         }"""
 
-        challenge = base64url_to_bytes("4ab7dfd1-a695-4777-985f-ad2993828e99")
-        rp_id = "dev.dontneeda.pw"
-        expected_origin = "https://dev.dontneeda.pw"
+        challenge = base64url_to_bytes("t4LWI0iYJSTWPl9WXUdNhdHAnrPDLF9eWAP9lHgmHP8")
+        rp_id = "localhost"
+        expected_origin = "http://localhost:8000"
 
         verification = verify_registration_response(
             credential=credential,
@@ -39,5 +42,5 @@ class TestVerifyRegistrationResponseAndroidKey(TestCase):
 
         assert verification.fmt == AttestationFormat.ANDROID_KEY
         assert verification.credential_id == base64url_to_bytes(
-            "V51GE29tGbhby7sbg1cZ_qL8V8njqEsXpAnwQBobvgw"
+            "AYNe4CBKc8H30FuAb8uaht6JbEQfbSBnS0SX7B6MFg8ofI92oR5lheRDJCgwY-JqB_QSJtezdhMbf8Wzt_La5N0"
         )
diff --git a/tests/test_verify_registration_response_android_safetynet.py b/tests/test_verify_registration_response_android_safetynet.py
index 9eaa7f7..72104e7 100644
--- a/tests/test_verify_registration_response_android_safetynet.py
+++ b/tests/test_verify_registration_response_android_safetynet.py
@@ -2,6 +2,8 @@ from unittest import TestCase
 from unittest.mock import MagicMock, patch
 
 from webauthn.helpers import parse_attestation_object, parse_registration_credential_json
+from webauthn.helpers.structs import AttestationStatement
+from webauthn.helpers.exceptions import InvalidRegistrationResponse
 from webauthn.registration.formats.android_safetynet import (
     verify_android_safetynet,
 )
@@ -45,3 +47,143 @@ class TestVerifyRegistrationResponseAndroidSafetyNet(TestCase):
         )
 
         assert verified is True
+
+    @patch("OpenSSL.crypto.X509StoreContext.verify_certificate")
+    @patch("base64.b64encode")
+    @patch("cbor2.loads")
+    def test_verify_attestation_android_safetynet_basic_integrity_true_cts_profile_match_false(
+        self,
+        mock_cbor2_loads: MagicMock,
+        mock_b64encode: MagicMock,
+        mock_verify_certificate: MagicMock,
+    ):
+        """
+        We're not working with a full WebAuthn response here so we have to mock out some values
+        because all we really want to test is that such a response is allowed through
+        """
+        mock_cbor2_loads.return_value = {"authData": bytes()}
+        mock_b64encode.return_value = "3N7YJmISsFM0cdvMAYcHcw==".encode("utf-8")
+        mock_verify_certificate.return_value = True
+
+        # basicIntegrity: True, ctsProfileMatch: False
+        jws_result_only_fail_cts_check = (
+            "eyJ4NWMiOiBbIk1JSURXekNDQWtNQ0FRb3dEUVlKS29aSWh2Y05BUUVMQlFBd2NqRUxNQWtHQT"
+            "FVRUJoTUNWVk14Q3pBSkJnTlZCQWdNQWsxSk1Rc3dDUVlEVlFRSERBSkJRVEVVTUJJR0ExVUVD"
+            "Z3dMUkhWdlUyVmpkWEpwZEhreEdUQVhCZ05WQkFzTUVGTmhabVYwZVU1bGRGUmxjM1JwYm1jeE"
+            "dEQVdCZ05WQkFNTUQxTmhabVYwZVc1bGRGUmxjM1JEUVRBZUZ3MHhPVEV3TVRneU1ESTJOVEZh"
+            "RncwME5qQXpNakF5TURJMk5URmFNSFV4Q3pBSkJnTlZCQVlUQWxWVE1Rc3dDUVlEVlFRSURBSk"
+            "5TVEVMTUFrR0ExVUVCd3dDUVVFeEZEQVNCZ05WQkFvTUMwUjFiMU5sWTNWeWFYUjVNUmt3RndZ"
+            "RFZRUUxEQkJUWVdabGRIbE9aWFJVWlhOMGFXNW5NUnN3R1FZRFZRUUREQkpoZEhSbGMzUXVZVz"
+            "VrY205cFpDNWpiMjB3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFR"
+            "RGNvL0dIbDQzNU8yQkZlTlVhdDJtbi9ZNVdGMk1RQWZiUWxwcVVWc2xVTjlZTnJSV1FYVFlYN3"
+            "pLTjE3U2RRcmVaU05uZTN2dDVWY0o2ZjZNK1o3NGRTUHpnOVBlN3dSWEVxMVk2aDNEQWVEdGN6"
+            "VGZGdWdOM3ArRWJhK01LcWkxL29UOHpzUysyL3RzVnpDVTJjNDd5QlQrT1ZRYTBTaUJsRjJlej"
+            "F3QkQ1VFFJRCt4VjJwOWNmWW5sYzBYSmpnZzFyRGVuR05xUm9zdERqeDJqTWViWG5vK05RM2Zj"
+            "N21HTHFrb2R0QmZEbWNHcHhxM3c5alBQQy9jbTZTaHlNM2g5ZXR1bzdHbFZVelhuOXQ3djU4RX"
+            "ZKTWJySkc2MHorT0ZTYTlJbG93U3NYMDlPbzBlaHhYRlpLbklXdisyMGtVNE1IcVZKcDIzeFhi"
+            "SElXZS9uZndEQWdNQkFBRXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBRmpzVVJCRXJOMGUwNU"
+            "JZRlMyRUU3YkZ5VnNVMUhQT0NNZ21TN0s0Nm1OZFdGUXZNT3lzdEdrUkQ3S2YramlxdmF6eWVy"
+            "NUdVbllSOXZCZVJrVko3TkZmY2gvd05JKzBwaTdUNDk2WmNBM0JKemp4STVxcnROZFBIY1FsNk"
+            "dLQThHZmQ1ZzNFQ3JUNjhoN1paQ2hJUXlHVUxKTVdwVkljL3dPT1FqNTNieEZQRTJ0U0VWQlhp"
+            "SUx6Tnh4NkxuZUwxaWdaMEZzdVdoU3dnRVArVXA0WFBYN3ZQbXZoczBDb3pUOHNXbG9BOEJzbG"
+            "dDZGlpeVI5ZThGQTR5RG5VTTFRWnBxMkFNUlBMc3ZJcDVnQndXYVNnejQxaUo0Qk5pOE1rWkJP"
+            "cklBckV0UzVxYzFIamN4ZklXaURoUjR5MTJqcEhud1Y0ZXpVdHNtVCtwdjFpVUQwUWVzPSJdLC"
+            "AiYWxnIjogIlJTMjU2In0.eyJ0aW1lc3RhbXBNcyI6IDE1NDM4NDk4NjQ5NzAsICJhZHZpY2Ui"
+            "OiAiTE9DS19CT09UTE9BREVSIiwgIm5vbmNlIjogIjNON1lKbUlTc0ZNMGNkdk1BWWNIY3c9PS"
+            "IsICJhcGtQYWNrYWdlTmFtZSI6ICJjb20uZHVvc2VjdXJpdHkuZHVvbW9iaWxlLmRlYnVnIiwg"
+            "ImFwa0RpZ2VzdFNoYTI1NiI6ICJIVm94ZlBNM1BwYkJaQkRmcWxORGt0Lyt3aXNhTTlrY0Exb2"
+            "l1NEhabDZJPSIsICJjdHNQcm9maWxlTWF0Y2giOiBmYWxzZSwgImJhc2ljSW50ZWdyaXR5Ijog"
+            "dHJ1ZSwgImFwa0NlcnRpZmljYXRlRGlnZXN0U2hhMjU2IjogWyJweUVSMGp6cnJWcU1KdW1pUW"
+            "pGUjdSVS9SdEVLbGkxckxGUEVUMGpPZnlrPSJdfQ.WJhEXK1a2mNycdH_bYYkXhvkADLRsxLaX"
+            "RzglwYpQXKgHuJap6x1UmWkFiygrgbd6jFfRGqGhifjubgfjHMkrMOJhA723hJNKKvfp-voZYS"
+            "TILmFsb1LrXjYyaut8V1POWgt3cw4HKfWXgKE2hw-KGkaD9Mrq1vBfXn8LSEkJsv7TyGtkiIbW"
+            "cYw0wEym7H6CyVFygwyx2B7fVz02Y15IYjz7NuHj3f9OMCScO70mGrvw7BPwaVs4LSNv8zEFOg"
+            "S2W1MzvpXwq1KMFvrcka7C4t5vyOhMMYwY6BWEnAGcx5_tpJsqegXTgTHSrr4TFQJzsa-H8wb1"
+            "YaxlMcRVSqOew"
+        )
+
+        attestation_statement = AttestationStatement(
+            ver="0",
+            response=jws_result_only_fail_cts_check.encode("ascii"),
+        )
+
+        verified = verify_android_safetynet(
+            attestation_statement=attestation_statement,
+            attestation_object=bytes(),
+            client_data_json=bytes(),
+            pem_root_certs_bytes=[],
+            verify_timestamp_ms=False,
+        )
+
+        assert verified is True
+
+    @patch("OpenSSL.crypto.X509StoreContext.verify_certificate")
+    @patch("base64.b64encode")
+    @patch("cbor2.loads")
+    def test_raise_attestation_android_safetynet_basic_integrity_false_cts_profile_match_false(
+        self,
+        mock_cbor2_loads: MagicMock,
+        mock_b64encode: MagicMock,
+        mock_verify_certificate: MagicMock,
+    ):
+        """
+        We're not working with a full WebAuthn response here so we have to mock out some values
+        because all we really want to test is that a response fails the basicIntegrity check
+        """
+        mock_cbor2_loads.return_value = {"authData": bytes()}
+        mock_b64encode.return_value = "NumMA+QH27ik6Mu737RgWg==".encode("utf-8")
+        mock_verify_certificate.return_value = True
+
+        # basicIntegrity: False, ctsProfileMatch: False
+        jws_result_fail = (
+            "eyJ4NWMiOiBbIk1JSURXekNDQWtNQ0FRb3dEUVlKS29aSWh2Y05BUUVMQlFBd2NqRUxNQWtHQT"
+            "FVRUJoTUNWVk14Q3pBSkJnTlZCQWdNQWsxSk1Rc3dDUVlEVlFRSERBSkJRVEVVTUJJR0ExVUVD"
+            "Z3dMUkhWdlUyVmpkWEpwZEhreEdUQVhCZ05WQkFzTUVGTmhabVYwZVU1bGRGUmxjM1JwYm1jeE"
+            "dEQVdCZ05WQkFNTUQxTmhabVYwZVc1bGRGUmxjM1JEUVRBZUZ3MHhPVEV3TVRneU1ESTJOVEZh"
+            "RncwME5qQXpNakF5TURJMk5URmFNSFV4Q3pBSkJnTlZCQVlUQWxWVE1Rc3dDUVlEVlFRSURBSk"
+            "5TVEVMTUFrR0ExVUVCd3dDUVVFeEZEQVNCZ05WQkFvTUMwUjFiMU5sWTNWeWFYUjVNUmt3RndZ"
+            "RFZRUUxEQkJUWVdabGRIbE9aWFJVWlhOMGFXNW5NUnN3R1FZRFZRUUREQkpoZEhSbGMzUXVZVz"
+            "VrY205cFpDNWpiMjB3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFR"
+            "RGNvL0dIbDQzNU8yQkZlTlVhdDJtbi9ZNVdGMk1RQWZiUWxwcVVWc2xVTjlZTnJSV1FYVFlYN3"
+            "pLTjE3U2RRcmVaU05uZTN2dDVWY0o2ZjZNK1o3NGRTUHpnOVBlN3dSWEVxMVk2aDNEQWVEdGN6"
+            "VGZGdWdOM3ArRWJhK01LcWkxL29UOHpzUysyL3RzVnpDVTJjNDd5QlQrT1ZRYTBTaUJsRjJlej"
+            "F3QkQ1VFFJRCt4VjJwOWNmWW5sYzBYSmpnZzFyRGVuR05xUm9zdERqeDJqTWViWG5vK05RM2Zj"
+            "N21HTHFrb2R0QmZEbWNHcHhxM3c5alBQQy9jbTZTaHlNM2g5ZXR1bzdHbFZVelhuOXQ3djU4RX"
+            "ZKTWJySkc2MHorT0ZTYTlJbG93U3NYMDlPbzBlaHhYRlpLbklXdisyMGtVNE1IcVZKcDIzeFhi"
+            "SElXZS9uZndEQWdNQkFBRXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBRmpzVVJCRXJOMGUwNU"
+            "JZRlMyRUU3YkZ5VnNVMUhQT0NNZ21TN0s0Nm1OZFdGUXZNT3lzdEdrUkQ3S2YramlxdmF6eWVy"
+            "NUdVbllSOXZCZVJrVko3TkZmY2gvd05JKzBwaTdUNDk2WmNBM0JKemp4STVxcnROZFBIY1FsNk"
+            "dLQThHZmQ1ZzNFQ3JUNjhoN1paQ2hJUXlHVUxKTVdwVkljL3dPT1FqNTNieEZQRTJ0U0VWQlhp"
+            "SUx6Tnh4NkxuZUwxaWdaMEZzdVdoU3dnRVArVXA0WFBYN3ZQbXZoczBDb3pUOHNXbG9BOEJzbG"
+            "dDZGlpeVI5ZThGQTR5RG5VTTFRWnBxMkFNUlBMc3ZJcDVnQndXYVNnejQxaUo0Qk5pOE1rWkJP"
+            "cklBckV0UzVxYzFIamN4ZklXaURoUjR5MTJqcEhud1Y0ZXpVdHNtVCtwdjFpVUQwUWVzPSJdLC"
+            "AiYWxnIjogIlJTMjU2In0.eyJ0aW1lc3RhbXBNcyI6IDE1NDM4NTAzNjAyMTQsICJhZHZpY2Ui"
+            "OiAiUkVTVE9SRV9UT19GQUNUT1JZX1JPTSIsICJub25jZSI6ICJOdW1NQStRSDI3aWs2TXU3Mz"
+            "dSZ1dnPT0iLCAiYXBrUGFja2FnZU5hbWUiOiAiY29tLmR1b3NlY3VyaXR5LmR1b21vYmlsZS5k"
+            "ZWJ1ZyIsICJhcGtEaWdlc3RTaGEyNTYiOiAiYzhFd2NMQUVRNHIycVlzanBDdE9NOUR1QjZyZ0"
+            "E3WWxjTXBOZm9kSHo0bz0iLCAiY3RzUHJvZmlsZU1hdGNoIjogZmFsc2UsICJiYXNpY0ludGVn"
+            "cml0eSI6IGZhbHNlLCAiYXBrQ2VydGlmaWNhdGVEaWdlc3RTaGEyNTYiOiBbInB5RVIwanpycl"
+            "ZxTUp1bWlRakZSN1JVL1J0RUtsaTFyTEZQRVQwak9meWs9Il19.UgwRHy2UMio8eN2Y994Kyzi"
+            "wqlpzDwRIybYiem4dj8BYWC3Ta48BAR0NN45TDdsGvDGUujVo0LSayfTcgo-vbilz5Y7LWCEgb"
+            "GoAFhoDDPAMPtthrYTnGDVfhjHTVo00AxsZVgL-HZOD0KecqWcOL8-DWARl3rTAjBWqfon7ZC2"
+            "IaxzJVrcWtyhPyKdzVB5hJ4NPKIAPlCUkMVUzPY9Xhg1DFLmvaIv8qcZo8xpY0JZDm9cxR1GwP"
+            "4OVdwMd5seh5483VEpqAmzX7NcZ0aoiMl5PhLGgzHZTrsd1Mc-RZqgc3hAYjnubxONN8vOWGzP"
+            "gI2Vzgr4VzLOZsWfYwKSR5g"
+        )
+
+        attestation_statement = AttestationStatement(
+            ver="0",
+            response=jws_result_fail.encode("ascii"),
+        )
+
+        with self.assertRaisesRegex(
+            InvalidRegistrationResponse,
+            "Could not verify device integrity",
+        ):
+            verify_android_safetynet(
+                attestation_statement=attestation_statement,
+                attestation_object=bytes(),
+                client_data_json=bytes(),
+                pem_root_certs_bytes=[],
+                verify_timestamp_ms=False,
+            )