Title: [254356] trunk
Revision
254356
Author
[email protected]
Date
2020-01-10 11:06:44 -0800 (Fri, 10 Jan 2020)

Log Message

[WebAuthn] Support authenticatorGetNextAssertion
https://bugs.webkit.org/show_bug.cgi?id=203346
<rdar://problem/56558488>

Reviewed by Brent Fulgham.

Source/WebCore:

Covered by new tests within existing test files.

* Modules/webauthn/AuthenticatorAssertionResponse.h:
(WebCore::AuthenticatorAssertionResponse::setName):
(WebCore::AuthenticatorAssertionResponse::name const):
(WebCore::AuthenticatorAssertionResponse::setDisplayName):
(WebCore::AuthenticatorAssertionResponse::displayName const):
(WebCore::AuthenticatorAssertionResponse::setNumberOfCredentials):
(WebCore::AuthenticatorAssertionResponse::numberOfCredentials const):
Adds new members to store new fields of the response from the authenticator. Field "icon"
is omitted given it could be used to track users according to https://github.com/w3c/webauthn/issues/1285.
* Modules/webauthn/fido/DeviceResponseConverter.cpp:
(fido::readCTAPGetAssertionResponse):
Adds new logic to parse above fields from an authenticator response.

Source/WebKit:

This patch implements authenticatorGetNextAssertion as suggested by the spec:
https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#authenticatorGetNextAssertion

The work flow is as follow:
1) When a valid assertion response is received, check its numberOfCredentials member;
2) When it is larger then 1, use authenticatorGetNextAssertion to get all remaining responses;
3) Once all responses are gathered, ask UI clients to pick one to return.

* UIProcess/API/APIWebAuthenticationPanelClient.h:
(API::WebAuthenticationPanelClient::selectAssertionResponses const):
* UIProcess/WebAuthentication/Authenticator.h:
* UIProcess/WebAuthentication/AuthenticatorManager.cpp:
(WebKit::AuthenticatorManager::selectAssertionResponses):
* UIProcess/WebAuthentication/AuthenticatorManager.h:
* UIProcess/WebAuthentication/Mock/MockHidConnection.cpp:
(WebKit::MockHidConnection::parseRequest):
* UIProcess/WebAuthentication/fido/CtapAuthenticator.cpp:
(WebKit::CtapAuthenticator::continueGetAssertionAfterResponseReceived):
(WebKit::CtapAuthenticator::continueGetNextAssertionAfterResponseReceived):
* UIProcess/WebAuthentication/fido/CtapAuthenticator.h:

Tools:

* TestWebKitAPI/Tests/WebCore/CtapResponseTest.cpp:
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/WebCore/FidoTestData.h:
Adds new test case for new logic in DeviceResponseConverter.

LayoutTests:

* 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-hid.https-expected.txt:
* http/wpt/webauthn/public-key-credential-get-success-hid.https.html:
* http/wpt/webauthn/resources/util.js:

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (254355 => 254356)


--- trunk/LayoutTests/ChangeLog	2020-01-10 19:02:26 UTC (rev 254355)
+++ trunk/LayoutTests/ChangeLog	2020-01-10 19:06:44 UTC (rev 254356)
@@ -1,3 +1,17 @@
+2020-01-10  Jiewen Tan  <[email protected]>
+
+        [WebAuthn] Support authenticatorGetNextAssertion
+        https://bugs.webkit.org/show_bug.cgi?id=203346
+        <rdar://problem/56558488>
+
+        Reviewed by Brent Fulgham.
+
+        * 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-hid.https-expected.txt:
+        * http/wpt/webauthn/public-key-credential-get-success-hid.https.html:
+        * http/wpt/webauthn/resources/util.js:
+
 2020-01-10  Brent Fulgham  <[email protected]>
 
         Remove 'com.apple.nehelper' from the WebContent sandbox.

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


--- trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid.https-expected.txt	2020-01-10 19:02:26 UTC (rev 254355)
+++ trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid.https-expected.txt	2020-01-10 19:06:44 UTC (rev 254356)
@@ -5,4 +5,5 @@
 PASS PublicKeyCredential's [[get]] with authenticator downgrade failed in a mock hid authenticator. 
 PASS PublicKeyCredential's [[get]] with authenticator downgrade failed in a mock hid authenticator. 2 
 PASS PublicKeyCredential's [[get]] with authenticator downgrade succeeded and then U2F failed in a mock hid authenticator. 
+PASS PublicKeyCredential's [[get]] with getNextAssertion failed in a mock hid authenticator. 
 

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


--- trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid.https.html	2020-01-10 19:02:26 UTC (rev 254355)
+++ trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid.https.html	2020-01-10 19:06:44 UTC (rev 254356)
@@ -84,4 +84,16 @@
             internals.setMockWebAuthenticationConfiguration({ hid: { stage: "request", subStage: "msg", error: "malicious-payload", canDowngrade: true, payloadBase64: [testCtapErrInvalidCredentialResponseBase64, testU2fApduNoErrorOnlyResponseBase64] } });
         return promiseRejects(t, "UnknownError", navigator.credentials.get(options), "Unknown internal error. Error code: 34");
     }, "PublicKeyCredential's [[get]] with authenticator downgrade succeeded and then U2F failed in a mock hid authenticator.");
+
+    promise_test(function(t) {
+        const options = {
+            publicKey: {
+                challenge: asciiToUint8Array("123456")
+            }
+        };
+
+        if (window.internals)
+            internals.setMockWebAuthenticationConfiguration({ hid: { stage: "request", subStage: "msg", error: "malicious-payload", payloadBase64: [testAssertionMessageLongBase64, testCtapErrNotAllowedResponseBase64] } });
+        return promiseRejects(t, "UnknownError", navigator.credentials.get(options), "Unknown internal error. Error code: 48");
+    }, "PublicKeyCredential's [[get]] with getNextAssertion failed in a mock hid authenticator.");
 </script>

Modified: trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-hid.https-expected.txt (254355 => 254356)


--- trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-hid.https-expected.txt	2020-01-10 19:02:26 UTC (rev 254355)
+++ trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-hid.https-expected.txt	2020-01-10 19:06:44 UTC (rev 254356)
@@ -5,4 +5,5 @@
 PASS PublicKeyCredential's [[get]] with userVerification { discouraged } in a mock hid authenticator. 
 PASS PublicKeyCredential's [[get]] with mixed options in a mock hid authenticator. 
 PASS PublicKeyCredential's [[get]] with two consecutive requests. 
+PASS PublicKeyCredential's [[get]] with multiple accounts in a mock hid authenticator. 
 

Modified: trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-hid.https.html (254355 => 254356)


--- trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-hid.https.html	2020-01-10 19:02:26 UTC (rev 254355)
+++ trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-hid.https.html	2020-01-10 19:06:44 UTC (rev 254356)
@@ -101,4 +101,19 @@
             return checkCtapGetAssertionResult(credential);
         });
     }, "PublicKeyCredential's [[get]] with two consecutive requests.");
+
+    promise_test(t => {
+        const options = {
+            publicKey: {
+                challenge: Base64URL.parse("MTIzNDU2"),
+                timeout: 100
+            }
+        };
+
+        if (window.internals)
+            internals.setMockWebAuthenticationConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", payloadBase64: [testAssertionMessageLongBase64, testAssertionMessageLongBase64] } });
+        return navigator.credentials.get(options).then(credential => {
+            return checkCtapGetAssertionResult(credential, "MIIBkzCCATigAwIBAjCCAZMwggE4oAMCAQIwggGTMII=");
+        });
+    }, "PublicKeyCredential's [[get]] with multiple accounts in a mock hid authenticator.");
 </script>

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


--- trunk/LayoutTests/http/wpt/webauthn/resources/util.js	2020-01-10 19:02:26 UTC (rev 254355)
+++ trunk/LayoutTests/http/wpt/webauthn/resources/util.js	2020-01-10 19:06:44 UTC (rev 254356)
@@ -66,6 +66,15 @@
     "Z51VstuQkuHI2eXh0Ct1gPC0gSx3CWLh5I9a2AEAAABQA1hHMEUCIQCSFTuuBWgB" +
     "4/F0VB7DlUVM09IHPmxe1MzHUwRoCRZbCAIgGKov6xoAx2MEf6/6qNs8OutzhP2C" +
     "QoJ1L7Fe64G9uBc=";
+const testAssertionMessageLongBase64 =
+    "AKUBomJpZFhAKAitzuj+Tslzelf3/vZwIGtDQNgoKeFd5oEieYzhyzA65saf0tK2" +
+    "w/mooa7tQtGgDdwZIjOhjcuZ0pQ1ajoE4GR0eXBlanB1YmxpYy1rZXkCWCVGzH+5" +
+    "Z51VstuQkuHI2eXh0Ct1gPC0gSx3CWLh5I9a2AEAAABQA1hHMEUCIQCSFTuuBWgB" +
+    "4/F0VB7DlUVM09IHPmxe1MzHUwRoCRZbCAIgGKov6xoAx2MEf6/6qNs8OutzhP2C" +
+    "QoJ1L7Fe64G9uBcEpGJpZFggMIIBkzCCATigAwIBAjCCAZMwggE4oAMCAQIwggGT" +
+    "MIJkaWNvbngoaHR0cHM6Ly9waWNzLmFjbWUuY29tLzAwL3AvYUJqampwcVBiLnBu" +
+    "Z2RuYW1ldmpvaG5wc21pdGhAZXhhbXBsZS5jb21rZGlzcGxheU5hbWVtSm9obiBQ" +
+    "LiBTbWl0aAUC";
 const testU2fApduNoErrorOnlyResponseBase64 = "kAA=";
 const testU2fApduInsNotSupportedOnlyResponseBase64 = "bQA=";
 const testU2fApduWrongDataOnlyResponseBase64 = "aoA=";
@@ -96,6 +105,7 @@
     "f4V4LeEAhqeD0effTjY553H19q+jWq1Tc4WOkAA=";
 const testCtapErrCredentialExcludedOnlyResponseBase64 = "GQ==";
 const testCtapErrInvalidCredentialResponseBase64 = "Ig==";
+const testCtapErrNotAllowedResponseBase64 = "MA==";
 const testNfcU2fVersionBase64 = "VTJGX1YykAA=";
 const testNfcCtapVersionBase64 = "RklET18yXzCQAA==";
 const testGetInfoResponseApduBase64 =
@@ -394,7 +404,7 @@
         assert_equals(attestationObject.attStmt.x5c.length, 1);
 }
 
-function checkCtapGetAssertionResult(credential)
+function checkCtapGetAssertionResult(credential, userHandleBase64 = null)
 {
     // Check respond
     assert_array_equals(Base64URL.parse(credential.id), Base64URL.parse(testHidCredentialIdBase64));
@@ -401,7 +411,10 @@
     assert_equals(credential.type, 'public-key');
     assert_array_equals(new Uint8Array(credential.rawId), Base64URL.parse(testHidCredentialIdBase64));
     assert_equals(bytesToASCIIString(credential.response.clientDataJSON), '{"type":"webauthn.get","challenge":"MTIzNDU2","origin":"https://localhost:9443"}');
-    assert_equals(credential.response.userHandle, null);
+    if (userHandleBase64 == null)
+        assert_equals(credential.response.userHandle, null);
+    else
+        assert_array_equals(new Uint8Array(credential.response.userHandle), Base64URL.parse(userHandleBase64));
     assert_not_own_property(credential.getClientExtensionResults(), "appid");
 
     // Check authData

Modified: trunk/Source/WebCore/ChangeLog (254355 => 254356)


--- trunk/Source/WebCore/ChangeLog	2020-01-10 19:02:26 UTC (rev 254355)
+++ trunk/Source/WebCore/ChangeLog	2020-01-10 19:06:44 UTC (rev 254356)
@@ -1,3 +1,26 @@
+2020-01-10  Jiewen Tan  <[email protected]>
+
+        [WebAuthn] Support authenticatorGetNextAssertion
+        https://bugs.webkit.org/show_bug.cgi?id=203346
+        <rdar://problem/56558488>
+
+        Reviewed by Brent Fulgham.
+
+        Covered by new tests within existing test files.
+
+        * Modules/webauthn/AuthenticatorAssertionResponse.h:
+        (WebCore::AuthenticatorAssertionResponse::setName):
+        (WebCore::AuthenticatorAssertionResponse::name const):
+        (WebCore::AuthenticatorAssertionResponse::setDisplayName):
+        (WebCore::AuthenticatorAssertionResponse::displayName const):
+        (WebCore::AuthenticatorAssertionResponse::setNumberOfCredentials):
+        (WebCore::AuthenticatorAssertionResponse::numberOfCredentials const):
+        Adds new members to store new fields of the response from the authenticator. Field "icon"
+        is omitted given it could be used to track users according to https://github.com/w3c/webauthn/issues/1285.
+        * Modules/webauthn/fido/DeviceResponseConverter.cpp:
+        (fido::readCTAPGetAssertionResponse):
+        Adds new logic to parse above fields from an authenticator response.
+
 2020-01-10  Dean Jackson  <[email protected]>
 
         [WebGL] Add all remaining WebGL2 implementation functions to GraphicsContextGL

Modified: trunk/Source/WebCore/Modules/webauthn/AuthenticatorAssertionResponse.h (254355 => 254356)


--- trunk/Source/WebCore/Modules/webauthn/AuthenticatorAssertionResponse.h	2020-01-10 19:02:26 UTC (rev 254355)
+++ trunk/Source/WebCore/Modules/webauthn/AuthenticatorAssertionResponse.h	2020-01-10 19:06:44 UTC (rev 254356)
@@ -41,6 +41,13 @@
     ArrayBuffer* signature() const { return m_signature.ptr(); }
     ArrayBuffer* userHandle() const { return m_userHandle.get(); }
 
+    void setName(const String& name) { m_name = name; }
+    String name() const { return m_name; }
+    void setDisplayName(const String& displayName) { m_displayName = displayName; }
+    String displayName() const { return m_displayName; }
+    void setNumberOfCredentials(size_t numberOfCredentials) { m_numberOfCredentials = numberOfCredentials; }
+    size_t numberOfCredentials() const { return m_numberOfCredentials; }
+
 private:
     AuthenticatorAssertionResponse(Ref<ArrayBuffer>&&, Ref<ArrayBuffer>&&, Ref<ArrayBuffer>&&, RefPtr<ArrayBuffer>&&);
 
@@ -50,6 +57,10 @@
     Ref<ArrayBuffer> m_authenticatorData;
     Ref<ArrayBuffer> m_signature;
     RefPtr<ArrayBuffer> m_userHandle;
+
+    String m_name;
+    String m_displayName;
+    size_t m_numberOfCredentials { 0 };
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/Modules/webauthn/fido/DeviceResponseConverter.cpp (254355 => 254356)


--- trunk/Source/WebCore/Modules/webauthn/fido/DeviceResponseConverter.cpp	2020-01-10 19:02:26 UTC (rev 254355)
+++ trunk/Source/WebCore/Modules/webauthn/fido/DeviceResponseConverter.cpp	2020-01-10 19:06:44 UTC (rev 254356)
@@ -164,6 +164,7 @@
         return nullptr;
     auto& signature = it->second.getByteString();
 
+    RefPtr<AuthenticatorAssertionResponse> response;
     it = responseMap.find(CBOR(4));
     if (it != responseMap.end() && it->second.isMap()) {
         auto& user = it->second.getMap();
@@ -171,10 +172,30 @@
         if (itr == user.end() || !itr->second.isByteString())
             return nullptr;
         auto& userHandle = itr->second.getByteString();
-        return AuthenticatorAssertionResponse::create(credentialId, authData, signature, userHandle);
+        response = AuthenticatorAssertionResponse::create(credentialId, authData, signature, userHandle);
+
+        itr = user.find(CBOR(kEntityNameMapKey));
+        if (itr != user.end()) {
+            if (!itr->second.isString())
+                return nullptr;
+            response->setName(itr->second.getString());
+        }
+
+        itr = user.find(CBOR(kDisplayNameMapKey));
+        if (itr != user.end()) {
+            if (!itr->second.isString())
+                return nullptr;
+            response->setDisplayName(itr->second.getString());
+        }
+    } else {
+        response = AuthenticatorAssertionResponse::create(credentialId, authData, signature, { });
     }
 
-    return AuthenticatorAssertionResponse::create(credentialId, authData, signature, { });
+    it = responseMap.find(CBOR(5));
+    if (it != responseMap.end() && it->second.isUnsigned())
+        response->setNumberOfCredentials(it->second.getUnsigned());
+
+    return response;
 }
 
 Optional<AuthenticatorGetInfoResponse> readCTAPGetInfoResponse(const Vector<uint8_t>& inBuffer)

Modified: trunk/Source/WebKit/ChangeLog (254355 => 254356)


--- trunk/Source/WebKit/ChangeLog	2020-01-10 19:02:26 UTC (rev 254355)
+++ trunk/Source/WebKit/ChangeLog	2020-01-10 19:06:44 UTC (rev 254356)
@@ -1,3 +1,32 @@
+2020-01-10  Jiewen Tan  <[email protected]>
+
+        [WebAuthn] Support authenticatorGetNextAssertion
+        https://bugs.webkit.org/show_bug.cgi?id=203346
+        <rdar://problem/56558488>
+
+        Reviewed by Brent Fulgham.
+
+        This patch implements authenticatorGetNextAssertion as suggested by the spec:
+        https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#authenticatorGetNextAssertion
+
+        The work flow is as follow:
+        1) When a valid assertion response is received, check its numberOfCredentials member;
+        2) When it is larger then 1, use authenticatorGetNextAssertion to get all remaining responses;
+        3) Once all responses are gathered, ask UI clients to pick one to return.
+
+        * UIProcess/API/APIWebAuthenticationPanelClient.h:
+        (API::WebAuthenticationPanelClient::selectAssertionResponses const):
+        * UIProcess/WebAuthentication/Authenticator.h:
+        * UIProcess/WebAuthentication/AuthenticatorManager.cpp:
+        (WebKit::AuthenticatorManager::selectAssertionResponses):
+        * UIProcess/WebAuthentication/AuthenticatorManager.h:
+        * UIProcess/WebAuthentication/Mock/MockHidConnection.cpp:
+        (WebKit::MockHidConnection::parseRequest):
+        * UIProcess/WebAuthentication/fido/CtapAuthenticator.cpp:
+        (WebKit::CtapAuthenticator::continueGetAssertionAfterResponseReceived):
+        (WebKit::CtapAuthenticator::continueGetNextAssertionAfterResponseReceived):
+        * UIProcess/WebAuthentication/fido/CtapAuthenticator.h:
+
 2020-01-10  Brent Fulgham  <[email protected]>
 
         Remove 'com.apple.nehelper' from the WebContent sandbox.

Modified: trunk/Source/WebKit/UIProcess/API/APIWebAuthenticationPanelClient.h (254355 => 254356)


--- trunk/Source/WebKit/UIProcess/API/APIWebAuthenticationPanelClient.h	2020-01-10 19:02:26 UTC (rev 254355)
+++ trunk/Source/WebKit/UIProcess/API/APIWebAuthenticationPanelClient.h	2020-01-10 19:06:44 UTC (rev 254356)
@@ -27,6 +27,14 @@
 
 #if ENABLE(WEB_AUTHN)
 
+#include <wtf/CompletionHandler.h>
+#include <wtf/HashSet.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+class AuthenticatorAssertionResponse;
+}
+
 namespace WebKit {
 enum class WebAuthenticationStatus : bool;
 enum class WebAuthenticationResult : bool;
@@ -41,6 +49,7 @@
 
     virtual void updatePanel(WebKit::WebAuthenticationStatus) const { }
     virtual void dismissPanel(WebKit::WebAuthenticationResult) const { }
+    virtual void selectAssertionResponses(const HashSet<Ref<WebCore::AuthenticatorAssertionResponse>>& responses, CompletionHandler<void(const Ref<WebCore::AuthenticatorAssertionResponse>&)>&& completionHandler) const { completionHandler(*responses.begin()); }
 };
 
 } // namespace API

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/Authenticator.h (254355 => 254356)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/Authenticator.h	2020-01-10 19:02:26 UTC (rev 254355)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/Authenticator.h	2020-01-10 19:06:44 UTC (rev 254356)
@@ -35,6 +35,10 @@
 #include <wtf/RefCounted.h>
 #include <wtf/WeakPtr.h>
 
+namespace WebCore {
+class AuthenticatorAssertionResponse;
+}
+
 namespace WebKit {
 
 class Authenticator : public RefCounted<Authenticator>, public CanMakeWeakPtr<Authenticator> {
@@ -47,6 +51,7 @@
         virtual void respondReceived(Respond&&) = 0;
         virtual void downgrade(Authenticator* id, Ref<Authenticator>&& downgradedAuthenticator) = 0;
         virtual void authenticatorStatusUpdated(WebAuthenticationStatus) = 0;
+        virtual void selectAssertionResponses(const HashSet<Ref<WebCore::AuthenticatorAssertionResponse>>&, CompletionHandler<void(const Ref<WebCore::AuthenticatorAssertionResponse>&)>&&) = 0;
     };
 
     virtual ~Authenticator() = default;

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.cpp (254355 => 254356)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.cpp	2020-01-10 19:02:26 UTC (rev 254355)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.cpp	2020-01-10 19:06:44 UTC (rev 254356)
@@ -273,6 +273,12 @@
         panel->client().updatePanel(status);
 }
 
+void AuthenticatorManager::selectAssertionResponses(const HashSet<Ref<WebCore::AuthenticatorAssertionResponse>>& responses, CompletionHandler<void(const Ref<WebCore::AuthenticatorAssertionResponse>&)>&& completionHandler)
+{
+    if (auto* panel = m_pendingRequestData.panel.get())
+        panel->client().selectAssertionResponses(responses, WTFMove(completionHandler));
+}
+
 UniqueRef<AuthenticatorTransportService> AuthenticatorManager::createService(AuthenticatorTransport transport, AuthenticatorTransportService::Observer& observer) const
 {
     return AuthenticatorTransportService::create(transport, observer);

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.h (254355 => 254356)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.h	2020-01-10 19:02:26 UTC (rev 254355)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.h	2020-01-10 19:06:44 UTC (rev 254356)
@@ -81,6 +81,7 @@
     void respondReceived(Respond&&) final;
     void downgrade(Authenticator* id, Ref<Authenticator>&& downgradedAuthenticator) final;
     void authenticatorStatusUpdated(WebAuthenticationStatus) final;
+    void selectAssertionResponses(const HashSet<Ref<WebCore::AuthenticatorAssertionResponse>>&, CompletionHandler<void(const Ref<WebCore::AuthenticatorAssertionResponse>&)>&&) final;
 
     // Overriden by MockAuthenticatorManager.
     virtual UniqueRef<AuthenticatorTransportService> createService(WebCore::AuthenticatorTransport, AuthenticatorTransportService::Observer&) const;

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


--- trunk/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidConnection.cpp	2020-01-10 19:02:26 UTC (rev 254355)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidConnection.cpp	2020-01-10 19:06:44 UTC (rev 254356)
@@ -158,7 +158,7 @@
             auto cmd = static_cast<CtapRequestCommand>(payload[0]);
             payload.remove(0);
             auto requestMap = CBORReader::read(payload);
-            ASSERT(requestMap);
+            ASSERT(requestMap || cmd == CtapRequestCommand::kAuthenticatorGetNextAssertion);
 
             if (cmd == CtapRequestCommand::kAuthenticatorMakeCredential) {
                 auto it = requestMap->getMap().find(CBORValue(CtapMakeCredentialRequestOptionsKey)); // Find options.

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/fido/CtapAuthenticator.cpp (254355 => 254356)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/fido/CtapAuthenticator.cpp	2020-01-10 19:02:26 UTC (rev 254355)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/fido/CtapAuthenticator.cpp	2020-01-10 19:06:44 UTC (rev 254356)
@@ -100,9 +100,58 @@
         receiveRespond(ExceptionData { UnknownError, makeString("Unknown internal error. Error code: ", static_cast<uint8_t>(error)) });
         return;
     }
-    receiveRespond(response.releaseNonNull());
+
+    if (response->numberOfCredentials() <= 1) {
+        receiveRespond(response.releaseNonNull());
+        return;
+    }
+
+    m_remainingAssertionResponses = response->numberOfCredentials() - 1;
+    auto addResult = m_assertionResponses.add(response.releaseNonNull());
+    ASSERT_UNUSED(addResult, addResult.isNewEntry);
+    driver().transact(encodeEmptyAuthenticatorRequest(CtapRequestCommand::kAuthenticatorGetNextAssertion), [weakThis = makeWeakPtr(*this)](Vector<uint8_t>&& data) {
+        ASSERT(RunLoop::isMain());
+        if (!weakThis)
+            return;
+        weakThis->continueGetNextAssertionAfterResponseReceived(WTFMove(data));
+    });
 }
 
+void CtapAuthenticator::continueGetNextAssertionAfterResponseReceived(Vector<uint8_t>&& data)
+{
+    auto response = readCTAPGetAssertionResponse(data);
+    if (!response) {
+        auto error = getResponseCode(data);
+        receiveRespond(ExceptionData { UnknownError, makeString("Unknown internal error. Error code: ", static_cast<uint8_t>(error)) });
+        return;
+    }
+    m_remainingAssertionResponses--;
+    auto addResult = m_assertionResponses.add(response.releaseNonNull());
+    ASSERT_UNUSED(addResult, addResult.isNewEntry);
+
+    if (!m_remainingAssertionResponses) {
+        if (auto* observer = this->observer()) {
+            observer->selectAssertionResponses(m_assertionResponses, [this, weakThis = makeWeakPtr(*this)] (const Ref<AuthenticatorAssertionResponse>& response) {
+                ASSERT(RunLoop::isMain());
+                if (!weakThis)
+                    return;
+                auto returnResponse = m_assertionResponses.take(response);
+                if (!returnResponse)
+                    return;
+                receiveRespond(WTFMove(*returnResponse));
+            });
+        }
+        return;
+    }
+
+    driver().transact(encodeEmptyAuthenticatorRequest(CtapRequestCommand::kAuthenticatorGetNextAssertion), [weakThis = makeWeakPtr(*this)](Vector<uint8_t>&& data) {
+        ASSERT(RunLoop::isMain());
+        if (!weakThis)
+            return;
+        weakThis->continueGetNextAssertionAfterResponseReceived(WTFMove(data));
+    });
+}
+
 bool CtapAuthenticator::tryDowngrade()
 {
     if (m_info.versions().find(ProtocolVersion::kU2f) == m_info.versions().end())

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/fido/CtapAuthenticator.h (254355 => 254356)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/fido/CtapAuthenticator.h	2020-01-10 19:02:26 UTC (rev 254355)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/fido/CtapAuthenticator.h	2020-01-10 19:06:44 UTC (rev 254356)
@@ -48,6 +48,7 @@
     void continueMakeCredentialAfterResponseReceived(Vector<uint8_t>&&) const;
     void getAssertion() final;
     void continueGetAssertionAfterResponseReceived(Vector<uint8_t>&&);
+    void continueGetNextAssertionAfterResponseReceived(Vector<uint8_t>&&);
 
     bool tryDowngrade();
     bool processGoogleLegacyAppIdSupportExtension();
@@ -54,6 +55,8 @@
 
     fido::AuthenticatorGetInfoResponse m_info;
     bool m_isDowngraded { false };
+    size_t m_remainingAssertionResponses { 0 };
+    HashSet<Ref<WebCore::AuthenticatorAssertionResponse>> m_assertionResponses;
 };
 
 } // namespace WebKit

Modified: trunk/Tools/ChangeLog (254355 => 254356)


--- trunk/Tools/ChangeLog	2020-01-10 19:02:26 UTC (rev 254355)
+++ trunk/Tools/ChangeLog	2020-01-10 19:06:44 UTC (rev 254356)
@@ -1,3 +1,16 @@
+2020-01-10  Jiewen Tan  <[email protected]>
+
+        [WebAuthn] Support authenticatorGetNextAssertion
+        https://bugs.webkit.org/show_bug.cgi?id=203346
+        <rdar://problem/56558488>
+
+        Reviewed by Brent Fulgham.
+
+        * TestWebKitAPI/Tests/WebCore/CtapResponseTest.cpp:
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/Tests/WebCore/FidoTestData.h:
+        Adds new test case for new logic in DeviceResponseConverter.
+
 2020-01-10  Jonathan Bedard  <[email protected]>
 
         Python 3: Add support to run-webkit-tests (Follow-up fix)

Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/CtapResponseTest.cpp (254355 => 254356)


--- trunk/Tools/TestWebKitAPI/Tests/WebCore/CtapResponseTest.cpp	2020-01-10 19:02:26 UTC (rev 254355)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/CtapResponseTest.cpp	2020-01-10 19:06:44 UTC (rev 254356)
@@ -399,8 +399,19 @@
 
 // Leveraging example 5 of section 6.1 of the CTAP spec.
 // https://fidoalliance.org/specs/fido-v2.0-ps-20170927/fido-client-to-authenticator-protocol-v2.0-ps-20170927.html
-TEST(CTAPResponseTest, TestReadGetAssertionResponse)
+TEST(CTAPResponseTest, TestReadGetAssertionResponse1)
 {
+    auto getAssertionResponse = readCTAPGetAssertionResponse(convertBytesToVector(TestData::kDeviceGetAssertionResponseShort, sizeof(TestData::kDeviceGetAssertionResponseShort)));
+    ASSERT_TRUE(getAssertionResponse);
+
+    EXPECT_EQ(getAssertionResponse->authenticatorData()->byteLength(), sizeof(TestData::kCtap2GetAssertionAuthData));
+    EXPECT_EQ(memcmp(getAssertionResponse->authenticatorData()->data(), TestData::kCtap2GetAssertionAuthData, sizeof(TestData::kCtap2GetAssertionAuthData)), 0);
+    EXPECT_EQ(getAssertionResponse->signature()->byteLength(), sizeof(TestData::kCtap2GetAssertionSignature));
+    EXPECT_EQ(memcmp(getAssertionResponse->signature()->data(), TestData::kCtap2GetAssertionSignature, sizeof(TestData::kCtap2GetAssertionSignature)), 0);
+}
+
+TEST(CTAPResponseTest, TestReadGetAssertionResponse2)
+{
     auto getAssertionResponse = readCTAPGetAssertionResponse(convertBytesToVector(TestData::kDeviceGetAssertionResponse, sizeof(TestData::kDeviceGetAssertionResponse)));
     ASSERT_TRUE(getAssertionResponse);
 
@@ -408,8 +419,26 @@
     EXPECT_EQ(memcmp(getAssertionResponse->authenticatorData()->data(), TestData::kCtap2GetAssertionAuthData, sizeof(TestData::kCtap2GetAssertionAuthData)), 0);
     EXPECT_EQ(getAssertionResponse->signature()->byteLength(), sizeof(TestData::kCtap2GetAssertionSignature));
     EXPECT_EQ(memcmp(getAssertionResponse->signature()->data(), TestData::kCtap2GetAssertionSignature, sizeof(TestData::kCtap2GetAssertionSignature)), 0);
+    EXPECT_EQ(getAssertionResponse->userHandle()->byteLength(), sizeof(TestData::kCtap2GetAssertionUserHandle));
+    EXPECT_EQ(memcmp(getAssertionResponse->userHandle()->data(), TestData::kCtap2GetAssertionUserHandle, sizeof(TestData::kCtap2GetAssertionUserHandle)), 0);
 }
 
+TEST(CTAPResponseTest, TestReadGetAssertionResponse3)
+{
+    auto getAssertionResponse = readCTAPGetAssertionResponse(convertBytesToVector(TestData::kDeviceGetAssertionResponseLong, sizeof(TestData::kDeviceGetAssertionResponseLong)));
+    ASSERT_TRUE(getAssertionResponse);
+
+    EXPECT_EQ(getAssertionResponse->authenticatorData()->byteLength(), sizeof(TestData::kCtap2GetAssertionAuthData));
+    EXPECT_EQ(memcmp(getAssertionResponse->authenticatorData()->data(), TestData::kCtap2GetAssertionAuthData, sizeof(TestData::kCtap2GetAssertionAuthData)), 0);
+    EXPECT_EQ(getAssertionResponse->signature()->byteLength(), sizeof(TestData::kCtap2GetAssertionSignature));
+    EXPECT_EQ(memcmp(getAssertionResponse->signature()->data(), TestData::kCtap2GetAssertionSignature, sizeof(TestData::kCtap2GetAssertionSignature)), 0);
+    EXPECT_EQ(getAssertionResponse->userHandle()->byteLength(), sizeof(TestData::kCtap2GetAssertionUserHandle));
+    EXPECT_EQ(memcmp(getAssertionResponse->userHandle()->data(), TestData::kCtap2GetAssertionUserHandle, sizeof(TestData::kCtap2GetAssertionUserHandle)), 0);
+    EXPECT_STREQ(getAssertionResponse->name().utf8().data(), "[email protected]");
+    EXPECT_STREQ(getAssertionResponse->displayName().utf8().data(), "John P. Smith");
+    EXPECT_EQ(getAssertionResponse->numberOfCredentials(), 1u);
+}
+
 // Test that U2F register response is properly parsed.
 TEST(CTAPResponseTest, TestParseRegisterResponseData)
 {

Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/FidoTestData.h (254355 => 254356)


--- trunk/Tools/TestWebKitAPI/Tests/WebCore/FidoTestData.h	2020-01-10 19:02:26 UTC (rev 254355)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/FidoTestData.h	2020-01-10 19:06:44 UTC (rev 254356)
@@ -960,9 +960,133 @@
     0x74, 0xc7, 0x47, 0x90, 0xea, 0xab, 0xb1, 0x44, 0x11, 0xe7, 0xa0,
 };
 
+constexpr uint8_t kCtap2GetAssertionUserHandle[] = {
+    0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xa0, 0x03, 0x02, 0x01,
+    0x02, 0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xa0, 0x03, 0x02,
+    0x01, 0x02, 0x30, 0x82, 0x01, 0x93, 0x30, 0x82,
+};
+
+constexpr uint8_t kDeviceGetAssertionResponseShort[] = {
+    // Success response code
+    0x00,
+    // map(3)
+    0xa3,
+    // unsigned(1) - Credential
+    0x01,
+    // map(2)
+    0xa2,
+    // text(2)
+    0x62,
+    // "id"
+    0x69, 0x64,
+    // bytes(64)
+    0x58, 0x40,
+    // credential id
+    0xf2, 0x20, 0x06, 0xde, 0x4f, 0x90, 0x5a, 0xf6, 0x8a, 0x43, 0x94, 0x2f,
+    0x02, 0x4f, 0x2a, 0x5e, 0xce, 0x60, 0x3d, 0x9c, 0x6d, 0x4b, 0x3d, 0xf8,
+    0xbe, 0x08, 0xed, 0x01, 0xfc, 0x44, 0x26, 0x46, 0xd0, 0x34, 0x85, 0x8a,
+    0xc7, 0x5b, 0xed, 0x3f, 0xd5, 0x80, 0xbf, 0x98, 0x08, 0xd9, 0x4f, 0xcb,
+    0xee, 0x82, 0xb9, 0xb2, 0xef, 0x66, 0x77, 0xaf, 0x0a, 0xdc, 0xc3, 0x58,
+    0x52, 0xea, 0x6b, 0x9e,
+    // text(4)
+    0x64,
+    // "type"
+    0x74, 0x79, 0x70, 0x65,
+    // text(10)
+    0x6a,
+    // "public-key"
+    0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B, 0x65, 0x79,
+    // unsigned(2) - Auth data
+    0x02,
+    // bytes(37)
+    0x58, 0x25,
+    // auth data
+    0x62, 0x5d, 0xda, 0xdf, 0x74, 0x3f, 0x57, 0x27, 0xe6, 0x6b, 0xba, 0x8c,
+    0x2e, 0x38, 0x79, 0x22, 0xd1, 0xaf, 0x43, 0xc5, 0x03, 0xd9, 0x11, 0x4a,
+    0x8f, 0xba, 0x10, 0x4d, 0x84, 0xd0, 0x2b, 0xfa, 0x01, 0x00, 0x00, 0x00,
+    0x11,
+    // unsigned(3) - signature
+    0x03,
+    // bytes(71)
+    0x58, 0x47,
+    // signature
+    0x30, 0x45, 0x02, 0x20, 0x4a, 0x5a, 0x9d, 0xd3, 0x92, 0x98, 0x14, 0x9d,
+    0x90, 0x47, 0x69, 0xb5, 0x1a, 0x45, 0x14, 0x33, 0x00, 0x6f, 0x18, 0x2a,
+    0x34, 0xfb, 0xdf, 0x66, 0xde, 0x5f, 0xc7, 0x17, 0xd7, 0x5f, 0xb3, 0x50,
+    0x02, 0x21, 0x00, 0xa4, 0x6b, 0x8e, 0xa3, 0xc3, 0xb9, 0x33, 0x82, 0x1c,
+    0x6e, 0x7f, 0x5e, 0xf9, 0xda, 0xae, 0x94, 0xab, 0x47, 0xf1, 0x8d, 0xb4,
+    0x74, 0xc7, 0x47, 0x90, 0xea, 0xab, 0xb1, 0x44, 0x11, 0xe7, 0xa0,
+};
+
 constexpr uint8_t kDeviceGetAssertionResponse[] = {
     // Success response code
     0x00,
+    // map(4)
+    0xa4,
+    // unsigned(1) - Credential
+    0x01,
+    // map(2)
+    0xa2,
+    // text(2)
+    0x62,
+    // "id"
+    0x69, 0x64,
+    // bytes(64)
+    0x58, 0x40,
+    // credential id
+    0xf2, 0x20, 0x06, 0xde, 0x4f, 0x90, 0x5a, 0xf6, 0x8a, 0x43, 0x94, 0x2f,
+    0x02, 0x4f, 0x2a, 0x5e, 0xce, 0x60, 0x3d, 0x9c, 0x6d, 0x4b, 0x3d, 0xf8,
+    0xbe, 0x08, 0xed, 0x01, 0xfc, 0x44, 0x26, 0x46, 0xd0, 0x34, 0x85, 0x8a,
+    0xc7, 0x5b, 0xed, 0x3f, 0xd5, 0x80, 0xbf, 0x98, 0x08, 0xd9, 0x4f, 0xcb,
+    0xee, 0x82, 0xb9, 0xb2, 0xef, 0x66, 0x77, 0xaf, 0x0a, 0xdc, 0xc3, 0x58,
+    0x52, 0xea, 0x6b, 0x9e,
+    // text(4)
+    0x64,
+    // "type"
+    0x74, 0x79, 0x70, 0x65,
+    // text(10)
+    0x6a,
+    // "public-key"
+    0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B, 0x65, 0x79,
+    // unsigned(2) - Auth data
+    0x02,
+    // bytes(37)
+    0x58, 0x25,
+    // auth data
+    0x62, 0x5d, 0xda, 0xdf, 0x74, 0x3f, 0x57, 0x27, 0xe6, 0x6b, 0xba, 0x8c,
+    0x2e, 0x38, 0x79, 0x22, 0xd1, 0xaf, 0x43, 0xc5, 0x03, 0xd9, 0x11, 0x4a,
+    0x8f, 0xba, 0x10, 0x4d, 0x84, 0xd0, 0x2b, 0xfa, 0x01, 0x00, 0x00, 0x00,
+    0x11,
+    // unsigned(3) - signature
+    0x03,
+    // bytes(71)
+    0x58, 0x47,
+    // signature
+    0x30, 0x45, 0x02, 0x20, 0x4a, 0x5a, 0x9d, 0xd3, 0x92, 0x98, 0x14, 0x9d,
+    0x90, 0x47, 0x69, 0xb5, 0x1a, 0x45, 0x14, 0x33, 0x00, 0x6f, 0x18, 0x2a,
+    0x34, 0xfb, 0xdf, 0x66, 0xde, 0x5f, 0xc7, 0x17, 0xd7, 0x5f, 0xb3, 0x50,
+    0x02, 0x21, 0x00, 0xa4, 0x6b, 0x8e, 0xa3, 0xc3, 0xb9, 0x33, 0x82, 0x1c,
+    0x6e, 0x7f, 0x5e, 0xf9, 0xda, 0xae, 0x94, 0xab, 0x47, 0xf1, 0x8d, 0xb4,
+    0x74, 0xc7, 0x47, 0x90, 0xea, 0xab, 0xb1, 0x44, 0x11, 0xe7, 0xa0,
+    // unsigned(4) - publicKeyCredentialUserEntity
+    0x04,
+    // map(1)
+    0xa1,
+    // text(2)
+    0x62,
+    // "id"
+    0x69, 0x64,
+    // bytes(32) - user id
+    0x58, 0x20,
+    // user id
+    0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xa0, 0x03, 0x02, 0x01,
+    0x02, 0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xa0, 0x03, 0x02,
+    0x01, 0x02, 0x30, 0x82, 0x01, 0x93, 0x30, 0x82,
+};
+
+constexpr uint8_t kDeviceGetAssertionResponseLong[] = {
+    // Success response code
+    0x00,
     // map(5)
     0xa5,
     // unsigned(1) - Credential
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to