Title: [245500] trunk
Revision
245500
Author
[email protected]
Date
2019-05-18 17:42:16 -0700 (Sat, 18 May 2019)

Log Message

[WebAuthN] Allow authenticators that support both CTAP and U2F to try U2F if CTAP fails in authenticatorGetAssertion
https://bugs.webkit.org/show_bug.cgi?id=197974
<rdar://problem/50879746>

Reviewed by Brent Fulgham.

Source/WebKit:

Authenticators that support both CTAP and U2F protocols can be used in a U2F enabled browser to create a credential in
U2F format. When such authenticator is used to login in WebKit, it will be treated as a CTAP authenticator. Since the
previous credential is in U2F format, the authenticator will not consider that as a valid credential when CTAP requests
come along for that U2F credential. Therefore the previous created U2F credential will not be asked at all, and users
will not be able to login. This situation is not well documented in the CTAP/WebAuthN spec yet.

To workaround the above issue, an authenticator that supports both protocols will be downgraded to a U2F authenticator
to ask a potential U2F credential once a valid error is returned regarding to the first CTAP request.

* UIProcess/API/C/WKWebsiteDataStoreRef.cpp:
(WKWebsiteDataStoreSetWebAuthenticationMockConfiguration):
* UIProcess/WebAuthentication/Authenticator.h:
* UIProcess/WebAuthentication/AuthenticatorManager.cpp:
(WebKit::AuthenticatorManager::downgrade):
* UIProcess/WebAuthentication/AuthenticatorManager.h:
* UIProcess/WebAuthentication/Mock/MockHidConnection.cpp:
(WebKit::MockHidConnection::parseRequest):
(WebKit::MockHidConnection::feedReports):
* UIProcess/WebAuthentication/Mock/MockWebAuthenticationConfiguration.h:
* UIProcess/WebAuthentication/fido/CtapHidAuthenticator.cpp:
(WebKit::CtapHidAuthenticator::makeCredential):
(WebKit::CtapHidAuthenticator::getAssertion):
(WebKit::CtapHidAuthenticator::continueGetAssertionAfterResponseReceived):
(WebKit::CtapHidAuthenticator::tryDowngrade):
(WebKit::CtapHidAuthenticator::continueGetAssertionAfterResponseReceived const): Deleted.
* UIProcess/WebAuthentication/fido/CtapHidAuthenticator.h:

Tools:

Add a canDowngrade option for mock hid devices to simulate the situation.

* WebKitTestRunner/InjectedBundle/TestRunner.cpp:
(WTR::TestRunner::setWebAuthenticationMockConfiguration):

LayoutTests:

* http/wpt/webauthn/public-key-credential-get-failure-hid-silent.https-expected.txt:
* http/wpt/webauthn/public-key-credential-get-failure-hid-silent.https.html:
* http/wpt/webauthn/public-key-credential-get-failure-hid.https-expected.txt:
* http/wpt/webauthn/public-key-credential-get-failure-hid.https.html:
* http/wpt/webauthn/public-key-credential-get-success-u2f.https-expected.txt:
* http/wpt/webauthn/public-key-credential-get-success-u2f.https.html:
* http/wpt/webauthn/resources/util.js:

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (245499 => 245500)


--- trunk/LayoutTests/ChangeLog	2019-05-18 07:12:17 UTC (rev 245499)
+++ trunk/LayoutTests/ChangeLog	2019-05-19 00:42:16 UTC (rev 245500)
@@ -1,3 +1,19 @@
+2019-05-18  Jiewen Tan  <[email protected]>
+
+        [WebAuthN] Allow authenticators that support both CTAP and U2F to try U2F if CTAP fails in authenticatorGetAssertion
+        https://bugs.webkit.org/show_bug.cgi?id=197974
+        <rdar://problem/50879746>
+
+        Reviewed by Brent Fulgham.
+
+        * http/wpt/webauthn/public-key-credential-get-failure-hid-silent.https-expected.txt:
+        * http/wpt/webauthn/public-key-credential-get-failure-hid-silent.https.html:
+        * http/wpt/webauthn/public-key-credential-get-failure-hid.https-expected.txt:
+        * http/wpt/webauthn/public-key-credential-get-failure-hid.https.html:
+        * http/wpt/webauthn/public-key-credential-get-success-u2f.https-expected.txt:
+        * http/wpt/webauthn/public-key-credential-get-success-u2f.https.html:
+        * http/wpt/webauthn/resources/util.js:
+
 2019-05-17  Joonghun Park  <[email protected]>
 
         Implement CSS `display: flow-root` (modern clearfix)

Modified: trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid-silent.https-expected.txt (245499 => 245500)


--- trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid-silent.https-expected.txt	2019-05-18 07:12:17 UTC (rev 245499)
+++ trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid-silent.https-expected.txt	2019-05-19 00:42:16 UTC (rev 245500)
@@ -1,4 +1,7 @@
 
 PASS PublicKeyCredential's [[get]] with malicious payload in a mock hid authenticator. 
 PASS PublicKeyCredential's [[get]] with unsupported options in a mock hid authenticator. 
+PASS PublicKeyCredential's [[get]] with invalid credential in a mock hid authenticator. 
+PASS PublicKeyCredential's [[get]] with authenticator downgrade in a mock hid authenticator. 
+PASS PublicKeyCredential's [[get]] with authenticator downgrade in a mock hid authenticator. 2 
 

Modified: trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid-silent.https.html (245499 => 245500)


--- trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid-silent.https.html	2019-05-18 07:12:17 UTC (rev 245499)
+++ trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid-silent.https.html	2019-05-19 00:42:16 UTC (rev 245500)
@@ -30,4 +30,44 @@
             testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, hid: { stage: "request", subStage: "msg", error: "unsupported-options" } });
         return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Operation timed out.");
     }, "PublicKeyCredential's [[get]] with unsupported options in a mock hid authenticator.");
+
+    promise_test(function(t) {
+        const options = {
+            publicKey: {
+                challenge: asciiToUint8Array("123456"),
+                timeout: 10
+            }
+        };
+
+        if (window.testRunner)
+            testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, hid: { stage: "request", subStage: "msg", error: "malicious-payload", payloadBase64: [testCtapErrInvalidCredentialResponseBase64] } });
+        return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Operation timed out.");
+    }, "PublicKeyCredential's [[get]] with invalid credential in a mock hid authenticator.");
+
+    promise_test(function(t) {
+        const options = {
+            publicKey: {
+                challenge: asciiToUint8Array("123456"),
+                timeout: 10
+            }
+        };
+
+        if (window.testRunner)
+            testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, hid: { stage: "request", subStage: "msg", error: "malicious-payload", canDowngrade: true, payloadBase64: [testCtapErrInvalidCredentialResponseBase64] } });
+        return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Operation timed out.");
+    }, "PublicKeyCredential's [[get]] with authenticator downgrade in a mock hid authenticator.");
+
+    promise_test(function(t) {
+        const options = {
+            publicKey: {
+                challenge: asciiToUint8Array("123456"),
+                extensions: { appid: "" },
+                timeout: 10
+            }
+        };
+
+        if (window.testRunner)
+            testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, hid: { stage: "request", subStage: "msg", error: "malicious-payload", canDowngrade: true, payloadBase64: [testCtapErrInvalidCredentialResponseBase64] } });
+        return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Operation timed out.");
+    }, "PublicKeyCredential's [[get]] with authenticator downgrade in a mock hid authenticator. 2");
 </script>

Modified: trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid.https-expected.txt (245499 => 245500)


--- trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid.https-expected.txt	2019-05-18 07:12:17 UTC (rev 245499)
+++ trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid.https-expected.txt	2019-05-19 00:42:16 UTC (rev 245500)
@@ -2,4 +2,6 @@
 PASS PublicKeyCredential's [[get]] with timeout in a mock hid authenticator. 
 PASS PublicKeyCredential's [[get]] with malicious payload in a mock hid authenticator. 
 PASS PublicKeyCredential's [[get]] with unsupported options in a mock hid authenticator. 
+PASS PublicKeyCredential's [[get]] with authenticator downgrade failed in a mock hid authenticator. 
+PASS PublicKeyCredential's [[get]] with authenticator downgrade succeeded and then U2F failed in a mock hid authenticator. 2 
 

Modified: trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid.https.html (245499 => 245500)


--- trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid.https.html	2019-05-18 07:12:17 UTC (rev 245499)
+++ trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid.https.html	2019-05-19 00:42:16 UTC (rev 245500)
@@ -33,7 +33,7 @@
 
         if (window.testRunner)
             testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "malicious-payload", payloadBase64: [testDummyMessagePayloadBase64] } });
-        return promiseRejects(t, "UnknownError", navigator.credentials.get(options), "Unknown internal error. Error code: -1");
+        return promiseRejects(t, "UnknownError", navigator.credentials.get(options), "Unknown internal error. Error code: 255");
     }, "PublicKeyCredential's [[get]] with malicious payload in a mock hid authenticator.");
 
     promise_test(function(t) {
@@ -48,4 +48,29 @@
             testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "unsupported-options" } });
         return promiseRejects(t, "UnknownError", navigator.credentials.get(options), "Unknown internal error. Error code: 43");
     }, "PublicKeyCredential's [[get]] with unsupported options in a mock hid authenticator.");
+
+    promise_test(function(t) {
+        const options = {
+            publicKey: {
+                challenge: asciiToUint8Array("123456")
+            }
+        };
+
+        if (window.testRunner)
+            testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "malicious-payload", payloadBase64: [testCtapErrInvalidCredentialResponseBase64] } });
+        return promiseRejects(t, "UnknownError", navigator.credentials.get(options), "Unknown internal error. Error code: 34");
+    }, "PublicKeyCredential's [[get]] with authenticator downgrade failed in a mock hid authenticator.");
+
+    promise_test(function(t) {
+        const options = {
+            publicKey: {
+                challenge: asciiToUint8Array("123456"),
+                extensions: { appid: "" }
+            }
+        };
+
+        if (window.testRunner)
+            testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "malicious-payload", canDowngrade: true, payloadBase64: [testCtapErrInvalidCredentialResponseBase64] } });
+        return promiseRejects(t, "NotSupportedError", navigator.credentials.get(options), "Cannot convert the request to U2F command.");
+    }, "PublicKeyCredential's [[get]] with authenticator downgrade succeeded and then U2F failed in a mock hid authenticator. 2");
 </script>

Modified: trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-u2f.https-expected.txt (245499 => 245500)


--- trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-u2f.https-expected.txt	2019-05-18 07:12:17 UTC (rev 245499)
+++ trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-u2f.https-expected.txt	2019-05-19 00:42:16 UTC (rev 245500)
@@ -8,4 +8,6 @@
 PASS PublicKeyCredential's [[get]] with an AppID in a mock hid authenticator. 
 PASS PublicKeyCredential's [[get]] with multiple credentials and AppID is not used in a mock hid authenticator. 
 PASS PublicKeyCredential's [[get]] with multiple credentials and AppID is used in a mock hid authenticator. 
+PASS PublicKeyCredential's [[get]] with downgraded authenticator in a mock hid authenticator. 
+PASS PublicKeyCredential's [[get]] with downgraded authenticator in a mock hid authenticator. (AppID) 
 

Modified: trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-u2f.https.html (245499 => 245500)


--- trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-u2f.https.html	2019-05-18 07:12:17 UTC (rev 245499)
+++ trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-u2f.https.html	2019-05-19 00:42:16 UTC (rev 245500)
@@ -181,4 +181,38 @@
         });
     }, "PublicKeyCredential's [[get]] with multiple credentials and AppID is used in a mock hid authenticator.");
 
+    promise_test(t => {
+        const options = {
+            publicKey: {
+                challenge: Base64URL.parse("MTIzNDU2"),
+                allowCredentials: [{ type: "public-key", id: Base64URL.parse(testU2fCredentialIdBase64) }],
+                timeout: 100,
+                extensions: { appid: "https://localhost:666/appid" }
+            }
+        };
+
+        if (window.testRunner)
+            testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", canDowngrade: true, payloadBase64: [testCtapErrInvalidCredentialResponseBase64, testU2fSignResponse] } });
+        return navigator.credentials.get(options).then(credential => {
+            return checkResult(credential);
+        });
+    }, "PublicKeyCredential's [[get]] with downgraded authenticator in a mock hid authenticator.");
+
+    promise_test(t => {
+        const options = {
+            publicKey: {
+                challenge: Base64URL.parse("MTIzNDU2"),
+                allowCredentials: [{ type: "public-key", id: Base64URL.parse(testU2fCredentialIdBase64) }],
+                timeout: 100,
+                extensions: { appid: "https://localhost:666/appid" }
+            }
+        };
+
+        if (window.testRunner)
+            testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", canDowngrade: true, payloadBase64: [testCtapErrInvalidCredentialResponseBase64, testU2fApduWrongDataOnlyResponseBase64, testU2fSignResponse] } });
+        return navigator.credentials.get(options).then(credential => {
+            return checkResult(credential, true, "7eabc5cc3251bdc59115ef87b5f7ee74cb03747e39ba8341748565cc129c0719");
+        });
+    }, "PublicKeyCredential's [[get]] with downgraded authenticator in a mock hid authenticator. (AppID)");
+
 </script>

Modified: trunk/LayoutTests/http/wpt/webauthn/resources/util.js (245499 => 245500)


--- trunk/LayoutTests/http/wpt/webauthn/resources/util.js	2019-05-18 07:12:17 UTC (rev 245499)
+++ trunk/LayoutTests/http/wpt/webauthn/resources/util.js	2019-05-19 00:42:16 UTC (rev 245500)
@@ -98,6 +98,7 @@
     "AQAAADswRAIge94KUqwfTIsn4AOjcM1mpMcRjdItVEeDX0W5nGhCP/cCIDxRe0eH" +
     "f4V4LeEAhqeD0effTjY553H19q+jWq1Tc4WOkAA=";
 const testCtapErrCredentialExcludedOnlyResponseBase64 = "GQ==";
+const testCtapErrInvalidCredentialResponseBase64 = "Ig==";
 
 const RESOURCES_DIR = "/WebKit/webauthn/resources/";
 

Modified: trunk/Source/WebKit/ChangeLog (245499 => 245500)


--- trunk/Source/WebKit/ChangeLog	2019-05-18 07:12:17 UTC (rev 245499)
+++ trunk/Source/WebKit/ChangeLog	2019-05-19 00:42:16 UTC (rev 245500)
@@ -1,3 +1,38 @@
+2019-05-18  Jiewen Tan  <[email protected]>
+
+        [WebAuthN] Allow authenticators that support both CTAP and U2F to try U2F if CTAP fails in authenticatorGetAssertion
+        https://bugs.webkit.org/show_bug.cgi?id=197974
+        <rdar://problem/50879746>
+
+        Reviewed by Brent Fulgham.
+
+        Authenticators that support both CTAP and U2F protocols can be used in a U2F enabled browser to create a credential in
+        U2F format. When such authenticator is used to login in WebKit, it will be treated as a CTAP authenticator. Since the
+        previous credential is in U2F format, the authenticator will not consider that as a valid credential when CTAP requests
+        come along for that U2F credential. Therefore the previous created U2F credential will not be asked at all, and users
+        will not be able to login. This situation is not well documented in the CTAP/WebAuthN spec yet.
+
+        To workaround the above issue, an authenticator that supports both protocols will be downgraded to a U2F authenticator
+        to ask a potential U2F credential once a valid error is returned regarding to the first CTAP request.
+
+        * UIProcess/API/C/WKWebsiteDataStoreRef.cpp:
+        (WKWebsiteDataStoreSetWebAuthenticationMockConfiguration):
+        * UIProcess/WebAuthentication/Authenticator.h:
+        * UIProcess/WebAuthentication/AuthenticatorManager.cpp:
+        (WebKit::AuthenticatorManager::downgrade):
+        * UIProcess/WebAuthentication/AuthenticatorManager.h:
+        * UIProcess/WebAuthentication/Mock/MockHidConnection.cpp:
+        (WebKit::MockHidConnection::parseRequest):
+        (WebKit::MockHidConnection::feedReports):
+        * UIProcess/WebAuthentication/Mock/MockWebAuthenticationConfiguration.h:
+        * UIProcess/WebAuthentication/fido/CtapHidAuthenticator.cpp:
+        (WebKit::CtapHidAuthenticator::makeCredential):
+        (WebKit::CtapHidAuthenticator::getAssertion):
+        (WebKit::CtapHidAuthenticator::continueGetAssertionAfterResponseReceived):
+        (WebKit::CtapHidAuthenticator::tryDowngrade):
+        (WebKit::CtapHidAuthenticator::continueGetAssertionAfterResponseReceived const): Deleted.
+        * UIProcess/WebAuthentication/fido/CtapHidAuthenticator.h:
+
 2019-05-17  Don Olmstead  <[email protected]>
 
         [CMake] Use builtin FindICU

Modified: trunk/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.cpp (245499 => 245500)


--- trunk/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.cpp	2019-05-18 07:12:17 UTC (rev 245499)
+++ trunk/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.cpp	2019-05-19 00:42:16 UTC (rev 245500)
@@ -652,6 +652,9 @@
         if (auto continueAfterErrorData = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(hidRef, adoptWK(WKStringCreateWithUTF8CString("ContinueAfterErrorData")).get())))
             hid.continueAfterErrorData = WKBooleanGetValue(continueAfterErrorData);
 
+        if (auto canDowngrade = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(hidRef, adoptWK(WKStringCreateWithUTF8CString("CanDowngrade")).get())))
+            hid.canDowngrade = WKBooleanGetValue(canDowngrade);
+
         configuration.hid = WTFMove(hid);
     }
 

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/Authenticator.h (245499 => 245500)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/Authenticator.h	2019-05-18 07:12:17 UTC (rev 245499)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/Authenticator.h	2019-05-19 00:42:16 UTC (rev 245500)
@@ -44,6 +44,7 @@
     public:
         virtual ~Observer() = default;
         virtual void respondReceived(Respond&&) = 0;
+        virtual void downgrade(Authenticator* id, Ref<Authenticator>&& downgradedAuthenticator) = 0;
     };
 
     virtual ~Authenticator() = default;

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.cpp (245499 => 245500)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.cpp	2019-05-18 07:12:17 UTC (rev 245499)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.cpp	2019-05-19 00:42:16 UTC (rev 245500)
@@ -208,6 +208,17 @@
     respondReceivedInternal(WTFMove(respond));
 }
 
+void AuthenticatorManager::downgrade(Authenticator* id, Ref<Authenticator>&& downgradedAuthenticator)
+{
+    RunLoop::main().dispatch([weakThis = makeWeakPtr(*this), id] {
+        if (!weakThis)
+            return;
+        auto removed = weakThis->m_authenticators.remove(id);
+        ASSERT_UNUSED(removed, removed);
+    });
+    authenticatorAdded(WTFMove(downgradedAuthenticator));
+}
+
 UniqueRef<AuthenticatorTransportService> AuthenticatorManager::createService(WebCore::AuthenticatorTransport transport, AuthenticatorTransportService::Observer& observer) const
 {
     return AuthenticatorTransportService::create(transport, observer);

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.h (245499 => 245500)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.h	2019-05-18 07:12:17 UTC (rev 245499)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.h	2019-05-19 00:42:16 UTC (rev 245500)
@@ -70,6 +70,7 @@
 
     // Authenticator::Observer
     void respondReceived(Respond&&) final;
+    void downgrade(Authenticator* id, Ref<Authenticator>&& downgradedAuthenticator) final;
 
     // Overriden by MockAuthenticatorManager.
     virtual UniqueRef<AuthenticatorTransportService> createService(WebCore::AuthenticatorTransport, AuthenticatorTransportService::Observer&) const;

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidConnection.cpp (245499 => 245500)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidConnection.cpp	2019-05-18 07:12:17 UTC (rev 245499)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidConnection.cpp	2019-05-19 00:42:16 UTC (rev 245500)
@@ -132,6 +132,8 @@
 
     if (m_stage == Mock::Stage::Request && m_subStage == Mock::SubStage::Msg) {
         // Make sure we issue different msg cmd for CTAP and U2F.
+        if (m_configuration.hid->canDowngrade && !m_configuration.hid->isU2f)
+            m_configuration.hid->isU2f = m_requestMessage->cmd() == FidoHidDeviceCommand::kMsg;
         ASSERT(m_configuration.hid->isU2f ^ (m_requestMessage->cmd() != FidoHidDeviceCommand::kMsg));
 
         // Set options.
@@ -208,7 +210,11 @@
 
     Optional<FidoHidMessage> message;
     if (m_stage == Mock::Stage::Info && m_subStage == Mock::SubStage::Msg) {
-        auto infoData = encodeAsCBOR(AuthenticatorGetInfoResponse({ ProtocolVersion::kCtap }, Vector<uint8_t>(aaguidLength, 0u)));
+        Vector<uint8_t> infoData;
+        if (m_configuration.hid->canDowngrade)
+            infoData = encodeAsCBOR(AuthenticatorGetInfoResponse({ ProtocolVersion::kCtap, ProtocolVersion::kU2f }, Vector<uint8_t>(aaguidLength, 0u)));
+        else
+            infoData = encodeAsCBOR(AuthenticatorGetInfoResponse({ ProtocolVersion::kCtap }, Vector<uint8_t>(aaguidLength, 0u)));
         infoData.insert(0, static_cast<uint8_t>(CtapDeviceResponseCode::kSuccess)); // Prepend status code.
         if (stagesMatch() && m_configuration.hid->error == Mock::Error::WrongChannelId)
             message = FidoHidMessage::create(m_currentChannel - 1, FidoHidDeviceCommand::kCbor, infoData);

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/Mock/MockWebAuthenticationConfiguration.h (245499 => 245500)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/Mock/MockWebAuthenticationConfiguration.h	2019-05-18 07:12:17 UTC (rev 245499)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/Mock/MockWebAuthenticationConfiguration.h	2019-05-19 00:42:16 UTC (rev 245500)
@@ -69,6 +69,7 @@
         bool keepAlive { false };
         bool fastDataArrival { false };
         bool continueAfterErrorData { false };
+        bool canDowngrade { false };
     };
 
     bool silentFailure { false };

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidAuthenticator.cpp (245499 => 245500)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidAuthenticator.cpp	2019-05-18 07:12:17 UTC (rev 245499)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidAuthenticator.cpp	2019-05-19 00:42:16 UTC (rev 245500)
@@ -29,6 +29,7 @@
 #if ENABLE(WEB_AUTHN) && PLATFORM(MAC)
 
 #include "CtapHidDriver.h"
+#include "U2fHidAuthenticator.h"
 #include <WebCore/DeviceRequestConverter.h>
 #include <WebCore/DeviceResponseConverter.h>
 #include <WebCore/ExceptionData.h>
@@ -49,6 +50,7 @@
 
 void CtapHidAuthenticator::makeCredential()
 {
+    ASSERT(!m_isDowngraded);
     auto cborCmd = encodeMakeCredenitalRequestAsCBOR(requestData().hash, requestData().creationOptions, m_info.options().userVerificationAvailability());
     m_driver->transact(WTFMove(cborCmd), [weakThis = makeWeakPtr(*this)](Vector<uint8_t>&& data) {
         ASSERT(RunLoop::isMain());
@@ -74,6 +76,7 @@
 
 void CtapHidAuthenticator::getAssertion()
 {
+    ASSERT(!m_isDowngraded);
     auto cborCmd = encodeGetAssertionRequestAsCBOR(requestData().hash, requestData().requestOptions, m_info.options().userVerificationAvailability());
     m_driver->transact(WTFMove(cborCmd), [weakThis = makeWeakPtr(*this)](Vector<uint8_t>&& data) {
         ASSERT(RunLoop::isMain());
@@ -83,16 +86,32 @@
     });
 }
 
-void CtapHidAuthenticator::continueGetAssertionAfterResponseReceived(Vector<uint8_t>&& data) const
+void CtapHidAuthenticator::continueGetAssertionAfterResponseReceived(Vector<uint8_t>&& data)
 {
     auto response = readCTAPGetAssertionResponse(data);
     if (!response) {
-        receiveRespond(ExceptionData { UnknownError, makeString("Unknown internal error. Error code: ", data.size() == 1 ? data[0] : -1) });
+        auto error = getResponseCode(data);
+        if (error != CtapDeviceResponseCode::kCtap2ErrInvalidCBOR && tryDowngrade())
+            return;
+        receiveRespond(ExceptionData { UnknownError, makeString("Unknown internal error. Error code: ", static_cast<uint8_t>(error)) });
         return;
     }
     receiveRespond(WTFMove(*response));
 }
 
+bool CtapHidAuthenticator::tryDowngrade()
+{
+    if (m_info.versions().find(ProtocolVersion::kU2f) == m_info.versions().end())
+        return false;
+    if (!observer())
+        return false;
+
+    m_isDowngraded = true;
+    m_driver->setProtocol(ProtocolVersion::kU2f);
+    observer()->downgrade(this, U2fHidAuthenticator::create(WTFMove(m_driver)));
+    return true;
+}
+
 } // namespace WebKit
 
 #endif // ENABLE(WEB_AUTHN) && PLATFORM(MAC)

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidAuthenticator.h (245499 => 245500)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidAuthenticator.h	2019-05-18 07:12:17 UTC (rev 245499)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidAuthenticator.h	2019-05-19 00:42:16 UTC (rev 245500)
@@ -47,10 +47,13 @@
     void makeCredential() final;
     void continueMakeCredentialAfterResponseReceived(Vector<uint8_t>&&) const;
     void getAssertion() final;
-    void continueGetAssertionAfterResponseReceived(Vector<uint8_t>&&) const;
+    void continueGetAssertionAfterResponseReceived(Vector<uint8_t>&&);
 
+    bool tryDowngrade();
+
     std::unique_ptr<CtapHidDriver> m_driver;
     fido::AuthenticatorGetInfoResponse m_info;
+    bool m_isDowngraded { false };
 };
 
 } // namespace WebKit

Modified: trunk/Tools/ChangeLog (245499 => 245500)


--- trunk/Tools/ChangeLog	2019-05-18 07:12:17 UTC (rev 245499)
+++ trunk/Tools/ChangeLog	2019-05-19 00:42:16 UTC (rev 245500)
@@ -1,3 +1,16 @@
+2019-05-18  Jiewen Tan  <[email protected]>
+
+        [WebAuthN] Allow authenticators that support both CTAP and U2F to try U2F if CTAP fails in authenticatorGetAssertion
+        https://bugs.webkit.org/show_bug.cgi?id=197974
+        <rdar://problem/50879746>
+
+        Reviewed by Brent Fulgham.
+
+        Add a canDowngrade option for mock hid devices to simulate the situation.
+
+        * WebKitTestRunner/InjectedBundle/TestRunner.cpp:
+        (WTR::TestRunner::setWebAuthenticationMockConfiguration):
+
 2019-05-18  Tadeu Zagallo  <[email protected]>
 
         Add extra information to dumpJITMemory

Modified: trunk/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp (245499 => 245500)


--- trunk/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp	2019-05-18 07:12:17 UTC (rev 245499)
+++ trunk/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp	2019-05-19 00:42:16 UTC (rev 245500)
@@ -2692,6 +2692,16 @@
             hidValues.append(adoptWK(WKBooleanCreate(continueAfterErrorData)).get());
         }
 
+        JSRetainPtr<JSStringRef> canDowngradePropertyName(Adopt, JSStringCreateWithUTF8CString("canDowngrade"));
+        JSValueRef canDowngradeValue = JSObjectGetProperty(context, hid, canDowngradePropertyName.get(), 0);
+        if (!JSValueIsUndefined(context, canDowngradeValue) && !JSValueIsNull(context, canDowngradeValue)) {
+            if (!JSValueIsBoolean(context, canDowngradeValue))
+                return;
+            bool canDowngrade = JSValueToBoolean(context, canDowngradeValue);
+            hidKeys.append(adoptWK(WKStringCreateWithUTF8CString("CanDowngrade")));
+            hidValues.append(adoptWK(WKBooleanCreate(canDowngrade)).get());
+        }
+
         Vector<WKStringRef> rawHidKeys;
         Vector<WKTypeRef> rawHidValues;
         rawHidKeys.resize(hidKeys.size());
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to