[WebAuthn] Implement AuthenticatorCancel
authorjiewen_tan@apple.com <jiewen_tan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 18 Oct 2019 20:55:56 +0000 (20:55 +0000)
committerjiewen_tan@apple.com <jiewen_tan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 18 Oct 2019 20:55:56 +0000 (20:55 +0000)
https://bugs.webkit.org/show_bug.cgi?id=191523
<rdar://problem/55920204>

Reviewed by Brent Fulgham.

Source/WebCore:

Covered by new tests in existing test files.

* Modules/credentialmanagement/CredentialsContainer.cpp:
(WebCore::CredentialsContainer::get):
(WebCore::CredentialsContainer::isCreate):
* Modules/webauthn/AuthenticatorCoordinator.cpp:
(WebCore::AuthenticatorCoordinator::create const):
(WebCore::AuthenticatorCoordinator::discoverFromExternalSource const):
* Modules/webauthn/AuthenticatorCoordinator.h:
* Modules/webauthn/AuthenticatorCoordinatorClient.cpp: Removed.
* Modules/webauthn/AuthenticatorCoordinatorClient.h:
* Modules/webauthn/PublicKeyCredential.cpp:
(WebCore::PublicKeyCredential::tryCreate):
* Modules/webauthn/PublicKeyCredential.h:
* Modules/webauthn/PublicKeyCredentialData.h:
* Modules/webauthn/fido/DeviceRequestConverter.h:
* Modules/webauthn/fido/FidoHidMessage.cpp:
(fido::FidoHidMessage::FidoHidMessage):
* Modules/webauthn/fido/FidoHidPacket.cpp:
(fido::FidoHidInitPacket::getSerializedData const):
(fido::FidoHidContinuationPacket::getSerializedData const):
* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:
* testing/MockWebAuthenticationConfiguration.h:
(WebCore::MockWebAuthenticationConfiguration::HidConfiguration::encode const):
(WebCore::MockWebAuthenticationConfiguration::HidConfiguration::decode):
* testing/MockWebAuthenticationConfiguration.idl:
Adds a new option to test AuthenticatorCancel.

Source/WebKit:

This patch implement two ways to cancel a pending WebAuthn ceremony:
1) Via navigation activities. Activities include i) main frame navigation, ii) main frame reload,
iii) main frame destruction, iv) sub frame navigation, and v) sub frame destruction. All the above
activities will cancel any pending WebAuthn ceremony that is associated with the frame. To prove
the association, a GlobalFrameIdentifier is bridged into WebAuthenticationRequestData. Navigation
cancel is done in WebPageProxy::didStartProvisionalLoadForFrameShared, and destruction cancel is done
in WebProcessProxy::didDestroyFrame and WebPageProxy::resetState.
2) Via UI. This path is simply bridged -[_WKWebAuthenticationPanel cancel] into AuthenticatorManager.
Noted, this patch follows the spec to wait until time out to notify RPs.
References: i) Step 20 of https://www.w3.org/TR/webauthn/#createCredential, ii) Step 18 of
https://www.w3.org/TR/webauthn/#getAssertion

As for what the cancel actually does, it:
1) stops any HID/NFC scanning;
2) sends CTAPHID_CANCEL to any HID authenticators that have been added. Reference:
https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#usb-hid-cancel
Sending CTAPHID_CANCEL, however, is not trivial. An abstract class FidoAuthenticator is crafted to
do this labor for both CtapAuthenticator and U2fAuthenticator during the time of destructions.
Noted: The CtapHidDriver is the only CtapDriver implements the cancel method. Since the message
is sent during state reset, lifecycle of the HidConenction and HidService which manage the underlying
IOHIDDeviceRef is very hard to hold. This is required for the regular async sender. Therefore,
HidConnection::sendSync is crafted to send the message synchronously to get rid of the tediousness
of managing those lifecycles.

P.S. Vector::grow doesn't initialize POD types. Therefore, this patch also appends it with memset
for FidoHidPacket.

P.S.S. This patch also simplifies AuthenticatorCoordinatorClient by: i) moving code from AuthenticatorCoordinatorClient
to WebAuthenticatorCoordinatorClient, and ii) using sendWithAsyncReply. The latter allows us to
get rid of the complex mechanism of ensuring the right reply is returned.

* DerivedSources.make:
* Sources.txt:
* UIProcess/API/APIWebAuthenticationPanel.cpp:
(API::WebAuthenticationPanel::create):
(API::WebAuthenticationPanel::WebAuthenticationPanel):
(API::WebAuthenticationPanel::cancel const):
* UIProcess/API/APIWebAuthenticationPanel.h:
* UIProcess/API/Cocoa/_WKWebAuthenticationPanel.mm:
(-[_WKWebAuthenticationPanel cancel]):
* UIProcess/WebAuthentication/AuthenticatorManager.cpp:
(WebKit::AuthenticatorManager::cancelRequest):
(WebKit::AuthenticatorManager::clearState):
(WebKit::AuthenticatorManager::runPanel):
(WebKit::AuthenticatorManager::resetState):
* UIProcess/WebAuthentication/AuthenticatorManager.h:
* UIProcess/WebAuthentication/Cocoa/HidConnection.h:
(WebKit::HidConnection::isInitialized const):
(WebKit::HidConnection::setIsInitialized):
* UIProcess/WebAuthentication/Cocoa/HidConnection.mm:
(WebKit::HidConnection::~HidConnection):
(WebKit::HidConnection::initialize):
(WebKit::HidConnection::terminate):
(WebKit::HidConnection::sendSync):
(WebKit::HidConnection::send):
(WebKit::HidConnection::registerDataReceivedCallback):
* UIProcess/WebAuthentication/Mock/MockHidConnection.cpp:
(WebKit::MockHidConnection::initialize):
(WebKit::MockHidConnection::terminate):
(WebKit::MockHidConnection::sendSync):
(WebKit::MockHidConnection::send):
(WebKit::MockHidConnection::feedReports):
* UIProcess/WebAuthentication/Mock/MockHidConnection.h:
* UIProcess/WebAuthentication/WebAuthenticationRequestData.h:
* UIProcess/WebAuthentication/WebAuthenticatorCoordinatorProxy.cpp:
(WebKit::WebAuthenticatorCoordinatorProxy::makeCredential):
(WebKit::WebAuthenticatorCoordinatorProxy::getAssertion):
(WebKit::WebAuthenticatorCoordinatorProxy::handleRequest):
(WebKit::WebAuthenticatorCoordinatorProxy::isUserVerifyingPlatformAuthenticatorAvailable):
(WebKit::WebAuthenticatorCoordinatorProxy::requestReply): Deleted.
* UIProcess/WebAuthentication/WebAuthenticatorCoordinatorProxy.h:
* UIProcess/WebAuthentication/WebAuthenticatorCoordinatorProxy.messages.in:
* UIProcess/WebAuthentication/fido/CtapAuthenticator.cpp:
(WebKit::CtapAuthenticator::CtapAuthenticator):
(WebKit::CtapAuthenticator::makeCredential):
(WebKit::CtapAuthenticator::getAssertion):
(WebKit::CtapAuthenticator::tryDowngrade):
* UIProcess/WebAuthentication/fido/CtapAuthenticator.h:
* UIProcess/WebAuthentication/fido/CtapDriver.h:
(WebKit::CtapDriver::cancel):
* UIProcess/WebAuthentication/fido/CtapHidDriver.cpp:
(WebKit::CtapHidDriver::Worker::write):
(WebKit::CtapHidDriver::Worker::read):
(WebKit::CtapHidDriver::Worker::returnMessage):
(WebKit::CtapHidDriver::Worker::reset):
(WebKit::CtapHidDriver::Worker::cancel):
(WebKit::CtapHidDriver::continueAfterChannelAllocated):
(WebKit::CtapHidDriver::continueAfterResponseReceived):
(WebKit::CtapHidDriver::returnResponse):
(WebKit::CtapHidDriver::reset):
(WebKit::CtapHidDriver::cancel):
* UIProcess/WebAuthentication/fido/CtapHidDriver.h:
* UIProcess/WebAuthentication/fido/FidoAuthenticator.cpp: Copied from Source/WebKit/UIProcess/API/APIWebAuthenticationPanel.cpp.
(WebKit::FidoAuthenticator::FidoAuthenticator):
(WebKit::FidoAuthenticator::~FidoAuthenticator):
(WebKit::FidoAuthenticator::driver const):
(WebKit::FidoAuthenticator::releaseDriver):
* UIProcess/WebAuthentication/fido/FidoAuthenticator.h: Copied from Source/WebKit/UIProcess/API/APIWebAuthenticationPanel.cpp.
* UIProcess/WebAuthentication/fido/FidoService.cpp:
(WebKit::FidoService::continueAfterGetInfo):
* UIProcess/WebAuthentication/fido/U2fAuthenticator.cpp:
(WebKit::U2fAuthenticator::U2fAuthenticator):
(WebKit::U2fAuthenticator::issueCommand):
* UIProcess/WebAuthentication/fido/U2fAuthenticator.h:
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::didStartProvisionalLoadForFrameShared):
(WebKit::WebPageProxy::resetState):
* UIProcess/WebProcessProxy.cpp:
(WebKit::WebProcessProxy::didDestroyFrame):
* WebKit.xcodeproj/project.pbxproj:
* WebProcess/WebAuthentication/WebAuthenticatorCoordinator.cpp:
(WebKit::WebAuthenticatorCoordinator::WebAuthenticatorCoordinator):
(WebKit::WebAuthenticatorCoordinator::makeCredential):
(WebKit::WebAuthenticatorCoordinator::getAssertion):
(WebKit::WebAuthenticatorCoordinator::isUserVerifyingPlatformAuthenticatorAvailable):
(WebKit::WebAuthenticatorCoordinator::~WebAuthenticatorCoordinator): Deleted.
* WebProcess/WebAuthentication/WebAuthenticatorCoordinator.h:
* WebProcess/WebAuthentication/WebAuthenticatorCoordinator.messages.in: Removed.
* WebProcess/WebPage/WebFrame.cpp:
(WebKit::WebFrame::fromCoreFrame):
* WebProcess/WebPage/WebFrame.h:

Tools:

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebCore/FidoHidMessageTest.cpp:
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/WebKitCocoa/_WKWebAuthenticationPanel.mm:
(-[TestWebAuthenticationPanelUIDelegate webView:runWebAuthenticationPanel:initiatedByFrame:completionHandler:]):
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-hid-cancel.html: Copied from Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-hid.html.
* TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-hid.html:
* TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-nfc.html:
* TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion.html:

LayoutTests:

Modified one of the error message that is no longer emitted.

* http/wpt/webauthn/public-key-credential-create-failure.https.html:
* http/wpt/webauthn/public-key-credential-create-success-hid.https.html:
* http/wpt/webauthn/public-key-credential-get-failure.https.html:
* http/wpt/webauthn/public-key-credential-get-success-hid.https.html:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@251295 268f45cc-cd09-0410-ab3c-d52691b4dbfc

62 files changed:
LayoutTests/ChangeLog
LayoutTests/http/wpt/webauthn/public-key-credential-create-failure.https.html
LayoutTests/http/wpt/webauthn/public-key-credential-create-success-hid.https.html
LayoutTests/http/wpt/webauthn/public-key-credential-get-failure.https.html
LayoutTests/http/wpt/webauthn/public-key-credential-get-success-hid.https.html
Source/WebCore/ChangeLog
Source/WebCore/Modules/credentialmanagement/CredentialsContainer.cpp
Source/WebCore/Modules/webauthn/AuthenticatorCoordinator.cpp
Source/WebCore/Modules/webauthn/AuthenticatorCoordinator.h
Source/WebCore/Modules/webauthn/AuthenticatorCoordinatorClient.h
Source/WebCore/Modules/webauthn/PublicKeyCredential.cpp
Source/WebCore/Modules/webauthn/PublicKeyCredential.h
Source/WebCore/Modules/webauthn/PublicKeyCredentialData.h
Source/WebCore/Modules/webauthn/fido/DeviceRequestConverter.h
Source/WebCore/Modules/webauthn/fido/FidoHidMessage.cpp
Source/WebCore/Modules/webauthn/fido/FidoHidPacket.cpp
Source/WebCore/Sources.txt
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/testing/MockWebAuthenticationConfiguration.h
Source/WebCore/testing/MockWebAuthenticationConfiguration.idl
Source/WebKit/ChangeLog
Source/WebKit/DerivedSources.make
Source/WebKit/Sources.txt
Source/WebKit/UIProcess/API/APIWebAuthenticationPanel.cpp
Source/WebKit/UIProcess/API/APIWebAuthenticationPanel.h
Source/WebKit/UIProcess/API/Cocoa/_WKWebAuthenticationPanel.mm
Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.cpp
Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.h
Source/WebKit/UIProcess/WebAuthentication/Cocoa/HidConnection.h
Source/WebKit/UIProcess/WebAuthentication/Cocoa/HidConnection.mm
Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidConnection.cpp
Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidConnection.h
Source/WebKit/UIProcess/WebAuthentication/WebAuthenticationRequestData.h
Source/WebKit/UIProcess/WebAuthentication/WebAuthenticatorCoordinatorProxy.cpp
Source/WebKit/UIProcess/WebAuthentication/WebAuthenticatorCoordinatorProxy.h
Source/WebKit/UIProcess/WebAuthentication/WebAuthenticatorCoordinatorProxy.messages.in
Source/WebKit/UIProcess/WebAuthentication/fido/CtapAuthenticator.cpp
Source/WebKit/UIProcess/WebAuthentication/fido/CtapAuthenticator.h
Source/WebKit/UIProcess/WebAuthentication/fido/CtapDriver.h
Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidDriver.cpp
Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidDriver.h
Source/WebKit/UIProcess/WebAuthentication/fido/FidoAuthenticator.cpp [moved from Source/WebCore/Modules/webauthn/AuthenticatorCoordinatorClient.cpp with 51% similarity]
Source/WebKit/UIProcess/WebAuthentication/fido/FidoAuthenticator.h [new file with mode: 0644]
Source/WebKit/UIProcess/WebAuthentication/fido/FidoService.cpp
Source/WebKit/UIProcess/WebAuthentication/fido/U2fAuthenticator.cpp
Source/WebKit/UIProcess/WebAuthentication/fido/U2fAuthenticator.h
Source/WebKit/UIProcess/WebPageProxy.cpp
Source/WebKit/UIProcess/WebProcessProxy.cpp
Source/WebKit/WebKit.xcodeproj/project.pbxproj
Source/WebKit/WebProcess/WebAuthentication/WebAuthenticatorCoordinator.cpp
Source/WebKit/WebProcess/WebAuthentication/WebAuthenticatorCoordinator.h
Source/WebKit/WebProcess/WebAuthentication/WebAuthenticatorCoordinator.messages.in [deleted file]
Source/WebKit/WebProcess/WebPage/WebFrame.cpp
Source/WebKit/WebProcess/WebPage/WebFrame.h
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebCore/FidoHidMessageTest.cpp
Tools/TestWebKitAPI/Tests/WebKitCocoa/_WKWebAuthenticationPanel.mm
Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-hid-cancel.html [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-hid.html
Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-nfc.html
Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion.html

index 114fbf4..0867816 100644 (file)
@@ -1,3 +1,18 @@
+2019-10-18  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthn] Implement AuthenticatorCancel
+        https://bugs.webkit.org/show_bug.cgi?id=191523
+        <rdar://problem/55920204>
+
+        Reviewed by Brent Fulgham.
+
+        Modified one of the error message that is no longer emitted.
+
+        * http/wpt/webauthn/public-key-credential-create-failure.https.html:
+        * http/wpt/webauthn/public-key-credential-create-success-hid.https.html:
+        * http/wpt/webauthn/public-key-credential-get-failure.https.html:
+        * http/wpt/webauthn/public-key-credential-get-success-hid.https.html:
+
 2019-10-18  Ryosuke Niwa  <rniwa@webkit.org>
 
         Flaky Test: fast/events/resize-subframe-in-rendering-update.html
index 5e8f5b8..1d6ed90 100644 (file)
@@ -87,8 +87,8 @@
             }
         };
 
-        const result = promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "This request has been voided by a new request.");
-        promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "This request has been voided by a new request.");
+        const result = promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "This request has been cancelled by a new request.");
+        promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "This request has been cancelled by a new request.");
         return result;
     }, "PublicKeyCredential's [[create]] with two consecutive requests");
 
                 pubKeyCredParams: [{ type: "public-key", alg: -7 }],
             }
         };
-        const result = promiseRejects(t, "NotAllowedError", navigator.credentials.create(creationOptions), "This request has been voided by a new request.");
+        const result = promiseRejects(t, "NotAllowedError", navigator.credentials.create(creationOptions), "This request has been cancelled by a new request.");
 
         const requestOptions = {
             publicKey: {
                 challenge: asciiToUint8Array("123456")
             }
         };
-        promiseRejects(t, "NotAllowedError", navigator.credentials.get(requestOptions), "This request has been voided by a new request.");
+        promiseRejects(t, "NotAllowedError", navigator.credentials.get(requestOptions), "This request has been cancelled by a new request.");
         return result;
     }, "PublicKeyCredential's [[create]] with two consecutive requests (2)");
 
index 5446f55..f64e4fc 100644 (file)
             }
         };
 
-        promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "This request has been voided by a new request.");
+        // Stall the first request to wait for cancellation.
+        if (window.internals)
+            internals.setMockWebAuthenticationConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", payloadBase64: [testCreationMessageBase64], expectCancel: true } });
+        promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "This request has been cancelled by a new request.");
+        if (window.internals)
+            internals.setMockWebAuthenticationConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", payloadBase64: [testCreationMessageBase64] } });
         return navigator.credentials.create(options).then(credential => {
             checkCtapMakeCredentialResult(credential);
         });
index cf18ff7..36b41f7 100644 (file)
@@ -87,8 +87,8 @@
             }
         };
 
-        const result = promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "This request has been voided by a new request.");
-        promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "This request has been voided by a new request.");
+        const result = promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "This request has been cancelled by a new request.");
+        promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "This request has been cancelled by a new request.");
         return result;
     }, "PublicKeyCredential's [[get]] with two consecutive requests");
 
@@ -98,7 +98,7 @@
                 challenge: asciiToUint8Array("123456")
             }
         };
-        const result = promiseRejects(t, "NotAllowedError", navigator.credentials.get(requestOptions), "This request has been voided by a new request.");
+        const result = promiseRejects(t, "NotAllowedError", navigator.credentials.get(requestOptions), "This request has been cancelled by a new request.");
 
         const creationOptions = {
             publicKey: {
                 pubKeyCredParams: [{ type: "public-key", alg: -7 }],
             }
         };
-        promiseRejects(t, "NotAllowedError", navigator.credentials.create(creationOptions), "This request has been voided by a new request.");
+        promiseRejects(t, "NotAllowedError", navigator.credentials.create(creationOptions), "This request has been cancelled by a new request.");
 
         return result;
     }, "PublicKeyCredential's [[get]] with two consecutive requests (2)");
index 0cf03e7..087c934 100644 (file)
@@ -90,7 +90,7 @@
             }
         };
 
-        promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "This request has been voided by a new request.");
+        promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "This request has been cancelled by a new request.");
         return navigator.credentials.get(options).then(credential => {
             return checkCtapGetAssertionResult(credential);
         });
index 8187fad..03e4c79 100644 (file)
@@ -1,3 +1,40 @@
+2019-10-18  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthn] Implement AuthenticatorCancel
+        https://bugs.webkit.org/show_bug.cgi?id=191523
+        <rdar://problem/55920204>
+
+        Reviewed by Brent Fulgham.
+
+        Covered by new tests in existing test files.
+
+        * Modules/credentialmanagement/CredentialsContainer.cpp:
+        (WebCore::CredentialsContainer::get):
+        (WebCore::CredentialsContainer::isCreate):
+        * Modules/webauthn/AuthenticatorCoordinator.cpp:
+        (WebCore::AuthenticatorCoordinator::create const):
+        (WebCore::AuthenticatorCoordinator::discoverFromExternalSource const):
+        * Modules/webauthn/AuthenticatorCoordinator.h:
+        * Modules/webauthn/AuthenticatorCoordinatorClient.cpp: Removed.
+        * Modules/webauthn/AuthenticatorCoordinatorClient.h:
+        * Modules/webauthn/PublicKeyCredential.cpp:
+        (WebCore::PublicKeyCredential::tryCreate):
+        * Modules/webauthn/PublicKeyCredential.h:
+        * Modules/webauthn/PublicKeyCredentialData.h:
+        * Modules/webauthn/fido/DeviceRequestConverter.h:
+        * Modules/webauthn/fido/FidoHidMessage.cpp:
+        (fido::FidoHidMessage::FidoHidMessage):
+        * Modules/webauthn/fido/FidoHidPacket.cpp:
+        (fido::FidoHidInitPacket::getSerializedData const):
+        (fido::FidoHidContinuationPacket::getSerializedData const):
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * testing/MockWebAuthenticationConfiguration.h:
+        (WebCore::MockWebAuthenticationConfiguration::HidConfiguration::encode const):
+        (WebCore::MockWebAuthenticationConfiguration::HidConfiguration::decode):
+        * testing/MockWebAuthenticationConfiguration.idl:
+        Adds a new option to test AuthenticatorCancel.
+
 2019-10-18  Simon Fraser  <simon.fraser@apple.com>
 
         Add TextStream dumping for BasicShape types
index 2f03895..d309d8b 100644 (file)
@@ -89,7 +89,7 @@ void CredentialsContainer::get(CredentialRequestOptions&& options, CredentialPro
         return;
     }
 
-    m_document->page()->authenticatorCoordinator().discoverFromExternalSource(m_document->securityOrigin(), options.publicKey.value(), doesHaveSameOriginAsItsAncestors(), WTFMove(options.signal), WTFMove(promise));
+    m_document->page()->authenticatorCoordinator().discoverFromExternalSource(*m_document, options.publicKey.value(), doesHaveSameOriginAsItsAncestors(), WTFMove(options.signal), WTFMove(promise));
 }
 
 void CredentialsContainer::store(const BasicCredential&, CredentialPromise&& promise)
@@ -124,7 +124,7 @@ void CredentialsContainer::isCreate(CredentialCreationOptions&& options, Credent
         return;
     }
 
-    m_document->page()->authenticatorCoordinator().create(m_document->securityOrigin(), options.publicKey.value(), doesHaveSameOriginAsItsAncestors(), WTFMove(options.signal), WTFMove(promise));
+    m_document->page()->authenticatorCoordinator().create(*m_document, options.publicKey.value(), doesHaveSameOriginAsItsAncestors(), WTFMove(options.signal), WTFMove(promise));
 }
 
 void CredentialsContainer::preventSilentAccess(DOMPromiseDeferred<void>&& promise) const
index 43a3df4..c64c9a3 100644 (file)
@@ -32,6 +32,7 @@
 #include "AuthenticatorAssertionResponse.h"
 #include "AuthenticatorAttestationResponse.h"
 #include "AuthenticatorCoordinatorClient.h"
+#include "Document.h"
 #include "JSBasicCredential.h"
 #include "JSDOMPromiseDeferred.h"
 #include "PublicKeyCredential.h"
@@ -135,10 +136,13 @@ void AuthenticatorCoordinator::setClient(std::unique_ptr<AuthenticatorCoordinato
     m_client = WTFMove(client);
 }
 
-void AuthenticatorCoordinator::create(const SecurityOrigin& callerOrigin, const PublicKeyCredentialCreationOptions& options, bool sameOriginWithAncestors, RefPtr<AbortSignal>&& abortSignal, CredentialPromise&& promise) const
+void AuthenticatorCoordinator::create(const Document& document, const PublicKeyCredentialCreationOptions& options, bool sameOriginWithAncestors, RefPtr<AbortSignal>&& abortSignal, CredentialPromise&& promise) const
 {
     using namespace AuthenticatorCoordinatorInternal;
 
+    const auto& callerOrigin = document.securityOrigin();
+    auto* frame = document.frame();
+    ASSERT(frame);
     // The following implements https://www.w3.org/TR/webauthn/#createCredential as of 5 December 2017.
     // Step 1, 3, 16 are handled by the caller.
     // Step 2.
@@ -185,14 +189,14 @@ void AuthenticatorCoordinator::create(const SecurityOrigin& callerOrigin, const
         return;
     }
 
-    auto completionHandler = [clientDataJson = WTFMove(clientDataJson), promise = WTFMove(promise), abortSignal = WTFMove(abortSignal)] (const WebCore::PublicKeyCredentialData& data, const WebCore::ExceptionData& exception) mutable {
+    auto callback = [clientDataJson = WTFMove(clientDataJson), promise = WTFMove(promise), abortSignal = WTFMove(abortSignal)] (PublicKeyCredentialData&& data, ExceptionData&& exception) mutable {
         if (abortSignal && abortSignal->aborted()) {
             promise.reject(Exception { AbortError, "Aborted by AbortSignal."_s });
             return;
         }
 
         data.clientDataJSON = WTFMove(clientDataJson);
-        if (auto publicKeyCredential = PublicKeyCredential::tryCreate(data)) {
+        if (auto publicKeyCredential = PublicKeyCredential::tryCreate(WTFMove(data))) {
             promise.resolve(publicKeyCredential.get());
             return;
         }
@@ -200,13 +204,16 @@ void AuthenticatorCoordinator::create(const SecurityOrigin& callerOrigin, const
         promise.reject(exception.toException());
     };
     // Async operations are dispatched and handled in the messenger.
-    m_client->makeCredential(clientDataJsonHash, options, WTFMove(completionHandler));
+    m_client->makeCredential(*frame, clientDataJsonHash, options, WTFMove(callback));
 }
 
-void AuthenticatorCoordinator::discoverFromExternalSource(const SecurityOrigin& callerOrigin, const PublicKeyCredentialRequestOptions& options, bool sameOriginWithAncestors, RefPtr<AbortSignal>&& abortSignal, CredentialPromise&& promise) const
+void AuthenticatorCoordinator::discoverFromExternalSource(const Document& document, const PublicKeyCredentialRequestOptions& options, bool sameOriginWithAncestors, RefPtr<AbortSignal>&& abortSignal, CredentialPromise&& promise) const
 {
     using namespace AuthenticatorCoordinatorInternal;
 
+    auto& callerOrigin = document.securityOrigin();
+    auto* frame = document.frame();
+    ASSERT(frame);
     // The following implements https://www.w3.org/TR/webauthn/#createCredential as of 5 December 2017.
     // Step 1, 3, 13 are handled by the caller.
     // Step 2.
@@ -253,14 +260,14 @@ void AuthenticatorCoordinator::discoverFromExternalSource(const SecurityOrigin&
         return;
     }
 
-    auto completionHandler = [clientDataJson = WTFMove(clientDataJson), promise = WTFMove(promise), abortSignal = WTFMove(abortSignal)] (const WebCore::PublicKeyCredentialData& data, const WebCore::ExceptionData& exception) mutable {
+    auto callback = [clientDataJson = WTFMove(clientDataJson), promise = WTFMove(promise), abortSignal = WTFMove(abortSignal)] (PublicKeyCredentialData&& data, ExceptionData&& exception) mutable {
         if (abortSignal && abortSignal->aborted()) {
             promise.reject(Exception { AbortError, "Aborted by AbortSignal."_s });
             return;
         }
 
         data.clientDataJSON = WTFMove(clientDataJson);
-        if (auto publicKeyCredential = PublicKeyCredential::tryCreate(data)) {
+        if (auto publicKeyCredential = PublicKeyCredential::tryCreate(WTFMove(data))) {
             promise.resolve(publicKeyCredential.get());
             return;
         }
@@ -268,7 +275,7 @@ void AuthenticatorCoordinator::discoverFromExternalSource(const SecurityOrigin&
         promise.reject(exception.toException());
     };
     // Async operations are dispatched and handled in the messenger.
-    m_client->getAssertion(clientDataJsonHash, options, WTFMove(completionHandler));
+    m_client->getAssertion(*frame, clientDataJsonHash, options, WTFMove(callback));
 }
 
 void AuthenticatorCoordinator::isUserVerifyingPlatformAuthenticatorAvailable(DOMPromiseDeferred<IDLBoolean>&& promise) const
index 9c65ae6..bdaa555 100644 (file)
@@ -36,7 +36,7 @@ namespace WebCore {
 class AbortSignal;
 class AuthenticatorCoordinatorClient;
 class BasicCredential;
-class SecurityOrigin;
+class Document;
 
 struct PublicKeyCredentialCreationOptions;
 struct PublicKeyCredentialRequestOptions;
@@ -53,8 +53,8 @@ public:
     WEBCORE_EXPORT void setClient(std::unique_ptr<AuthenticatorCoordinatorClient>&&);
 
     // The following methods implement static methods of PublicKeyCredential.
-    void create(const SecurityOrigin&, const PublicKeyCredentialCreationOptions&, bool sameOriginWithAncestors, RefPtr<AbortSignal>&&, CredentialPromise&&) const;
-    void discoverFromExternalSource(const SecurityOrigin&, const PublicKeyCredentialRequestOptions&, bool sameOriginWithAncestors, RefPtr<AbortSignal>&&, CredentialPromise&&) const;
+    void create(const Document&, const PublicKeyCredentialCreationOptions&, bool sameOriginWithAncestors, RefPtr<AbortSignal>&&, CredentialPromise&&) const;
+    void discoverFromExternalSource(const Document&, const PublicKeyCredentialRequestOptions&, bool sameOriginWithAncestors, RefPtr<AbortSignal>&&, CredentialPromise&&) const;
     void isUserVerifyingPlatformAuthenticatorAvailable(DOMPromiseDeferred<IDLBoolean>&&) const;
 
 private:
index 0330cda..b6cdb50 100644 (file)
 namespace WebCore {
 
 class DeferredPromise;
+class Frame;
 
 struct PublicKeyCredentialCreationOptions;
 struct PublicKeyCredentialData;
 struct PublicKeyCredentialRequestOptions;
 
-using RequestCompletionHandler = CompletionHandler<void(const WebCore::PublicKeyCredentialData&, const WebCore::ExceptionData&)>;
+using RequestCompletionHandler = CompletionHandler<void(WebCore::PublicKeyCredentialData&&, WebCore::ExceptionData&&)>;
 using QueryCompletionHandler = CompletionHandler<void(bool)>;
 
 class WEBCORE_EXPORT AuthenticatorCoordinatorClient : public CanMakeWeakPtr<AuthenticatorCoordinatorClient> {
@@ -50,26 +51,9 @@ public:
     AuthenticatorCoordinatorClient() = default;
     virtual ~AuthenticatorCoordinatorClient() = default;
 
-    // Senders.
-    virtual void makeCredential(const Vector<uint8_t>& hash, const PublicKeyCredentialCreationOptions&, RequestCompletionHandler&&) = 0;
-    virtual void getAssertion(const Vector<uint8_t>& hash, const PublicKeyCredentialRequestOptions&, RequestCompletionHandler&&) = 0;
-    virtual void isUserVerifyingPlatformAuthenticatorAvailable(QueryCompletionHandler&&) = 0;
-
-    // Receivers.
-    void requestReply(uint64_t messageId, const WebCore::PublicKeyCredentialData&, const WebCore::ExceptionData&);
-    void isUserVerifyingPlatformAuthenticatorAvailableReply(uint64_t messageId, bool);
-
-protected:
-    // Only one request is allowed at one time. A new request will cancel any pending request.
-    // A message id that is tied to the request wil be generated each time to prevent mismatching responses.
-    uint64_t setRequestCompletionHandler(RequestCompletionHandler&&);
-    uint64_t addQueryCompletionHandler(QueryCompletionHandler&&);
-
-private:
-    uint64_t m_accumulatedRequestMessageId { 1 };
-    RequestCompletionHandler m_pendingCompletionHandler;
-    uint64_t m_accumulatedQueryMessageId { 1 };
-    HashMap<uint64_t, QueryCompletionHandler> m_pendingQueryCompletionHandlers;
+    virtual void makeCredential(const Frame&, const Vector<uint8_t>&, const PublicKeyCredentialCreationOptions&, RequestCompletionHandler&&) { };
+    virtual void getAssertion(const Frame&, const Vector<uint8_t>&, const PublicKeyCredentialRequestOptions&, RequestCompletionHandler&&) { };
+    virtual void isUserVerifyingPlatformAuthenticatorAvailable(QueryCompletionHandler&&) { };
 };
 
 } // namespace WebCore
index bc282d4..8de600c 100644 (file)
@@ -41,7 +41,7 @@
 
 namespace WebCore {
 
-RefPtr<PublicKeyCredential> PublicKeyCredential::tryCreate(const PublicKeyCredentialData& data)
+RefPtr<PublicKeyCredential> PublicKeyCredential::tryCreate(PublicKeyCredentialData&& data)
 {
     if (!data.rawId || !data.clientDataJSON)
         return nullptr;
index 4be715b..d2a553a 100644 (file)
@@ -48,7 +48,7 @@ public:
         Optional<bool> appid;
     };
 
-    static RefPtr<PublicKeyCredential> tryCreate(const PublicKeyCredentialData&);
+    static RefPtr<PublicKeyCredential> tryCreate(PublicKeyCredentialData&&);
 
     ArrayBuffer* rawId() const { return m_rawId.ptr(); }
     AuthenticatorResponse* response() const { return m_response.ptr(); }
index 7e63ba3..6a054ef 100644 (file)
@@ -35,19 +35,19 @@ namespace WebCore {
 class AuthenticatorResponse;
 
 struct PublicKeyCredentialData {
-    mutable RefPtr<ArrayBuffer> rawId;
+    RefPtr<ArrayBuffer> rawId;
 
     // AuthenticatorResponse
     bool isAuthenticatorAttestationResponse;
-    mutable RefPtr<ArrayBuffer> clientDataJSON;
+    RefPtr<ArrayBuffer> clientDataJSON;
 
     // AuthenticatorAttestationResponse
-    mutable RefPtr<ArrayBuffer> attestationObject;
+    RefPtr<ArrayBuffer> attestationObject;
 
     // AuthenticatorAssertionResponse
-    mutable RefPtr<ArrayBuffer> authenticatorData;
-    mutable RefPtr<ArrayBuffer> signature;
-    mutable RefPtr<ArrayBuffer> userHandle;
+    RefPtr<ArrayBuffer> authenticatorData;
+    RefPtr<ArrayBuffer> signature;
+    RefPtr<ArrayBuffer> userHandle;
 
     // Extensions
     Optional<bool> appid;
index 4acc3ff..78114fa 100644 (file)
@@ -53,8 +53,7 @@ WEBCORE_EXPORT Vector<uint8_t> encodeMakeCredenitalRequestAsCBOR(const Vector<ui
 WEBCORE_EXPORT Vector<uint8_t> encodeGetAssertionRequestAsCBOR(const Vector<uint8_t>& hash, const WebCore::PublicKeyCredentialRequestOptions&, AuthenticatorSupportedOptions::UserVerificationAvailability);
 
 // Represents CTAP requests with empty parameters, including
-// AuthenticatorGetInfo, AuthenticatorCancel, AuthenticatorReset and
-// AuthenticatorGetNextAssertion commands.
+// AuthenticatorGetInfo, AuthenticatorReset and AuthenticatorGetNextAssertion commands.
 WEBCORE_EXPORT Vector<uint8_t> encodeEmptyAuthenticatorRequest(CtapRequestCommand);
 
 } // namespace fido
index 1b912d9..b12daef 100644 (file)
@@ -146,6 +146,7 @@ size_t FidoHidMessage::numPackets() const
 
 FidoHidMessage::FidoHidMessage(uint32_t channelId, FidoHidDeviceCommand type, const Vector<uint8_t>& data)
     : m_channelId(channelId)
+    , m_cmd(type)
 {
     uint8_t sequence = 0;
 
index 26b80aa..523220a 100644 (file)
@@ -99,7 +99,9 @@ Vector<uint8_t> FidoHidInitPacket::getSerializedData() const
     serialized.append((m_payloadLength >> 8) & 0xff);
     serialized.append(m_payloadLength & 0xff);
     serialized.append(m_data.begin(), m_data.size());
+    auto offset = serialized.size();
     serialized.grow(kHidPacketSize);
+    memset(serialized.data() + offset, 0, kHidPacketSize - offset);
 
     return serialized;
 }
@@ -147,7 +149,9 @@ Vector<uint8_t> FidoHidContinuationPacket::getSerializedData() const
     serialized.append(m_channelId & 0xff);
     serialized.append(m_sequence);
     serialized.append(m_data.begin(), m_data.size());
+    auto offset = serialized.size();
     serialized.grow(kHidPacketSize);
+    memset(serialized.data() + offset, 0, kHidPacketSize - offset);
 
     return serialized;
 }
index ffbda96..1726df5 100644 (file)
@@ -264,7 +264,6 @@ Modules/webaudio/WaveShaperNode.cpp
 Modules/webaudio/WaveShaperProcessor.cpp
 
 Modules/webauthn/AuthenticatorCoordinator.cpp
-Modules/webauthn/AuthenticatorCoordinatorClient.cpp
 Modules/webauthn/PublicKeyCredential.cpp
 Modules/webauthn/WebAuthenticationUtils.cpp
 Modules/webauthn/apdu/ApduCommand.cpp
index 9155483..e24ef4d 100644 (file)
                5760824F20118D8D00116678 /* JSBasicCredentialCustom.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = JSBasicCredentialCustom.cpp; sourceTree = "<group>"; };
                576082562011BE0200116678 /* JSAuthenticatorResponseCustom.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = JSAuthenticatorResponseCustom.cpp; sourceTree = "<group>"; };
                576082702021513F00116678 /* AuthenticatorCoordinatorClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AuthenticatorCoordinatorClient.h; sourceTree = "<group>"; };
-               57608293202BA95300116678 /* AuthenticatorCoordinatorClient.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AuthenticatorCoordinatorClient.cpp; sourceTree = "<group>"; };
                576814281E6F98AD00E77754 /* EcdhKeyDeriveParams.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = EcdhKeyDeriveParams.idl; sourceTree = "<group>"; };
                576814291E6F99C100E77754 /* CryptoAlgorithmEcdhKeyDeriveParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptoAlgorithmEcdhKeyDeriveParams.h; sourceTree = "<group>"; };
                576814341E6FE3E800E77754 /* CryptoAlgorithmECDHMac.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CryptoAlgorithmECDHMac.cpp; sourceTree = "<group>"; };
                                57303C1D2009A98600355965 /* AuthenticatorAttestationResponse.idl */,
                                57303C4420105B3D00355965 /* AuthenticatorCoordinator.cpp */,
                                57303C4320105B3D00355965 /* AuthenticatorCoordinator.h */,
-                               57608293202BA95300116678 /* AuthenticatorCoordinatorClient.cpp */,
                                576082702021513F00116678 /* AuthenticatorCoordinatorClient.h */,
                                57303BCF20087A8300355965 /* AuthenticatorResponse.h */,
                                57303BD120087A8300355965 /* AuthenticatorResponse.idl */,
index 7450f25..eb381cb 100644 (file)
@@ -83,6 +83,7 @@ struct MockWebAuthenticationConfiguration {
         bool fastDataArrival { false };
         bool continueAfterErrorData { false };
         bool canDowngrade { false };
+        bool expectCancel { false };
 
         template<class Encoder> void encode(Encoder&) const;
         template<class Decoder> static Optional<HidConfiguration> decode(Decoder&);
@@ -159,7 +160,7 @@ Optional<MockWebAuthenticationConfiguration::LocalConfiguration> MockWebAuthenti
 template<class Encoder>
 void MockWebAuthenticationConfiguration::HidConfiguration::encode(Encoder& encoder) const
 {
-    encoder << payloadBase64 << stage << subStage << error << isU2f << keepAlive << fastDataArrival << continueAfterErrorData << canDowngrade;
+    encoder << payloadBase64 << stage << subStage << error << isU2f << keepAlive << fastDataArrival << continueAfterErrorData << canDowngrade << expectCancel;
 }
 
 template<class Decoder>
@@ -184,6 +185,8 @@ Optional<MockWebAuthenticationConfiguration::HidConfiguration> MockWebAuthentica
         return WTF::nullopt;
     if (!decoder.decode(result.canDowngrade))
         return WTF::nullopt;
+    if (!decoder.decode(result.expectCancel))
+        return WTF::nullopt;
     return result;
 }
 
index bdeb861..16470f9 100644 (file)
@@ -91,6 +91,7 @@
     boolean fastDataArrival = false;
     boolean continueAfterErrorData = false;
     boolean canDowngrade = false;
+    boolean expectCancel = false;
 };
 
 [
index f9e6505..386eaaa 100644 (file)
@@ -1,3 +1,133 @@
+2019-10-18  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthn] Implement AuthenticatorCancel
+        https://bugs.webkit.org/show_bug.cgi?id=191523
+        <rdar://problem/55920204>
+
+        Reviewed by Brent Fulgham.
+
+        This patch implement two ways to cancel a pending WebAuthn ceremony:
+        1) Via navigation activities. Activities include i) main frame navigation, ii) main frame reload,
+        iii) main frame destruction, iv) sub frame navigation, and v) sub frame destruction. All the above
+        activities will cancel any pending WebAuthn ceremony that is associated with the frame. To prove
+        the association, a GlobalFrameIdentifier is bridged into WebAuthenticationRequestData. Navigation
+        cancel is done in WebPageProxy::didStartProvisionalLoadForFrameShared, and destruction cancel is done
+        in WebProcessProxy::didDestroyFrame and WebPageProxy::resetState.
+        2) Via UI. This path is simply bridged -[_WKWebAuthenticationPanel cancel] into AuthenticatorManager.
+        Noted, this patch follows the spec to wait until time out to notify RPs.
+        References: i) Step 20 of https://www.w3.org/TR/webauthn/#createCredential, ii) Step 18 of
+        https://www.w3.org/TR/webauthn/#getAssertion
+
+        As for what the cancel actually does, it:
+        1) stops any HID/NFC scanning;
+        2) sends CTAPHID_CANCEL to any HID authenticators that have been added. Reference:
+        https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#usb-hid-cancel
+        Sending CTAPHID_CANCEL, however, is not trivial. An abstract class FidoAuthenticator is crafted to
+        do this labor for both CtapAuthenticator and U2fAuthenticator during the time of destructions.
+        Noted: The CtapHidDriver is the only CtapDriver implements the cancel method. Since the message
+        is sent during state reset, lifecycle of the HidConenction and HidService which manage the underlying
+        IOHIDDeviceRef is very hard to hold. This is required for the regular async sender. Therefore,
+        HidConnection::sendSync is crafted to send the message synchronously to get rid of the tediousness
+        of managing those lifecycles.
+
+        P.S. Vector::grow doesn't initialize POD types. Therefore, this patch also appends it with memset
+        for FidoHidPacket.
+
+        P.S.S. This patch also simplifies AuthenticatorCoordinatorClient by: i) moving code from AuthenticatorCoordinatorClient
+        to WebAuthenticatorCoordinatorClient, and ii) using sendWithAsyncReply. The latter allows us to
+        get rid of the complex mechanism of ensuring the right reply is returned.
+
+        * DerivedSources.make:
+        * Sources.txt:
+        * UIProcess/API/APIWebAuthenticationPanel.cpp:
+        (API::WebAuthenticationPanel::create):
+        (API::WebAuthenticationPanel::WebAuthenticationPanel):
+        (API::WebAuthenticationPanel::cancel const):
+        * UIProcess/API/APIWebAuthenticationPanel.h:
+        * UIProcess/API/Cocoa/_WKWebAuthenticationPanel.mm:
+        (-[_WKWebAuthenticationPanel cancel]):
+        * UIProcess/WebAuthentication/AuthenticatorManager.cpp:
+        (WebKit::AuthenticatorManager::cancelRequest):
+        (WebKit::AuthenticatorManager::clearState):
+        (WebKit::AuthenticatorManager::runPanel):
+        (WebKit::AuthenticatorManager::resetState):
+        * UIProcess/WebAuthentication/AuthenticatorManager.h:
+        * UIProcess/WebAuthentication/Cocoa/HidConnection.h:
+        (WebKit::HidConnection::isInitialized const):
+        (WebKit::HidConnection::setIsInitialized):
+        * UIProcess/WebAuthentication/Cocoa/HidConnection.mm:
+        (WebKit::HidConnection::~HidConnection):
+        (WebKit::HidConnection::initialize):
+        (WebKit::HidConnection::terminate):
+        (WebKit::HidConnection::sendSync):
+        (WebKit::HidConnection::send):
+        (WebKit::HidConnection::registerDataReceivedCallback):
+        * UIProcess/WebAuthentication/Mock/MockHidConnection.cpp:
+        (WebKit::MockHidConnection::initialize):
+        (WebKit::MockHidConnection::terminate):
+        (WebKit::MockHidConnection::sendSync):
+        (WebKit::MockHidConnection::send):
+        (WebKit::MockHidConnection::feedReports):
+        * UIProcess/WebAuthentication/Mock/MockHidConnection.h:
+        * UIProcess/WebAuthentication/WebAuthenticationRequestData.h:
+        * UIProcess/WebAuthentication/WebAuthenticatorCoordinatorProxy.cpp:
+        (WebKit::WebAuthenticatorCoordinatorProxy::makeCredential):
+        (WebKit::WebAuthenticatorCoordinatorProxy::getAssertion):
+        (WebKit::WebAuthenticatorCoordinatorProxy::handleRequest):
+        (WebKit::WebAuthenticatorCoordinatorProxy::isUserVerifyingPlatformAuthenticatorAvailable):
+        (WebKit::WebAuthenticatorCoordinatorProxy::requestReply): Deleted.
+        * UIProcess/WebAuthentication/WebAuthenticatorCoordinatorProxy.h:
+        * UIProcess/WebAuthentication/WebAuthenticatorCoordinatorProxy.messages.in:
+        * UIProcess/WebAuthentication/fido/CtapAuthenticator.cpp:
+        (WebKit::CtapAuthenticator::CtapAuthenticator):
+        (WebKit::CtapAuthenticator::makeCredential):
+        (WebKit::CtapAuthenticator::getAssertion):
+        (WebKit::CtapAuthenticator::tryDowngrade):
+        * UIProcess/WebAuthentication/fido/CtapAuthenticator.h:
+        * UIProcess/WebAuthentication/fido/CtapDriver.h:
+        (WebKit::CtapDriver::cancel):
+        * UIProcess/WebAuthentication/fido/CtapHidDriver.cpp:
+        (WebKit::CtapHidDriver::Worker::write):
+        (WebKit::CtapHidDriver::Worker::read):
+        (WebKit::CtapHidDriver::Worker::returnMessage):
+        (WebKit::CtapHidDriver::Worker::reset):
+        (WebKit::CtapHidDriver::Worker::cancel):
+        (WebKit::CtapHidDriver::continueAfterChannelAllocated):
+        (WebKit::CtapHidDriver::continueAfterResponseReceived):
+        (WebKit::CtapHidDriver::returnResponse):
+        (WebKit::CtapHidDriver::reset):
+        (WebKit::CtapHidDriver::cancel):
+        * UIProcess/WebAuthentication/fido/CtapHidDriver.h:
+        * UIProcess/WebAuthentication/fido/FidoAuthenticator.cpp: Copied from Source/WebKit/UIProcess/API/APIWebAuthenticationPanel.cpp.
+        (WebKit::FidoAuthenticator::FidoAuthenticator):
+        (WebKit::FidoAuthenticator::~FidoAuthenticator):
+        (WebKit::FidoAuthenticator::driver const):
+        (WebKit::FidoAuthenticator::releaseDriver):
+        * UIProcess/WebAuthentication/fido/FidoAuthenticator.h: Copied from Source/WebKit/UIProcess/API/APIWebAuthenticationPanel.cpp.
+        * UIProcess/WebAuthentication/fido/FidoService.cpp:
+        (WebKit::FidoService::continueAfterGetInfo):
+        * UIProcess/WebAuthentication/fido/U2fAuthenticator.cpp:
+        (WebKit::U2fAuthenticator::U2fAuthenticator):
+        (WebKit::U2fAuthenticator::issueCommand):
+        * UIProcess/WebAuthentication/fido/U2fAuthenticator.h:
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::didStartProvisionalLoadForFrameShared):
+        (WebKit::WebPageProxy::resetState):
+        * UIProcess/WebProcessProxy.cpp:
+        (WebKit::WebProcessProxy::didDestroyFrame):
+        * WebKit.xcodeproj/project.pbxproj:
+        * WebProcess/WebAuthentication/WebAuthenticatorCoordinator.cpp:
+        (WebKit::WebAuthenticatorCoordinator::WebAuthenticatorCoordinator):
+        (WebKit::WebAuthenticatorCoordinator::makeCredential):
+        (WebKit::WebAuthenticatorCoordinator::getAssertion):
+        (WebKit::WebAuthenticatorCoordinator::isUserVerifyingPlatformAuthenticatorAvailable):
+        (WebKit::WebAuthenticatorCoordinator::~WebAuthenticatorCoordinator): Deleted.
+        * WebProcess/WebAuthentication/WebAuthenticatorCoordinator.h:
+        * WebProcess/WebAuthentication/WebAuthenticatorCoordinator.messages.in: Removed.
+        * WebProcess/WebPage/WebFrame.cpp:
+        (WebKit::WebFrame::fromCoreFrame):
+        * WebProcess/WebPage/WebFrame.h:
+
 2019-10-18  Zan Dobersek  <zdobersek@igalia.com>
 
         Avoid crashes on GCC-compiled binaries by avoiding a use-after-move
index bae7c31..c39c297 100644 (file)
@@ -146,7 +146,6 @@ MESSAGE_RECEIVERS = \
     ViewUpdateDispatcher \
     VisitedLinkStore \
     VisitedLinkTableController \
-    WebAuthenticatorCoordinator \
     WebAuthenticatorCoordinatorProxy \
     WebAutomationSession \
     WebAutomationSessionProxy \
index bf96c86..31ee118 100644 (file)
@@ -412,6 +412,7 @@ UIProcess/UserContent/WebUserContentControllerProxy.cpp
 UIProcess/WebAuthentication/fido/CtapAuthenticator.cpp
 UIProcess/WebAuthentication/fido/CtapHidDriver.cpp
 UIProcess/WebAuthentication/fido/CtapNfcDriver.cpp
+UIProcess/WebAuthentication/fido/FidoAuthenticator.cpp
 UIProcess/WebAuthentication/fido/FidoService.cpp
 UIProcess/WebAuthentication/fido/U2fAuthenticator.cpp
 
index 00d2d1a..87a2f06 100644 (file)
 #if ENABLE(WEB_AUTHN)
 
 #include "APIWebAuthenticationPanelClient.h"
+#include "AuthenticatorManager.h"
 
 namespace API {
+using namespace WebKit;
 
-Ref<WebAuthenticationPanel> WebAuthenticationPanel::create(const String& rpId)
+Ref<WebAuthenticationPanel> WebAuthenticationPanel::create(const AuthenticatorManager& manager, const String& rpId)
 {
-    return adoptRef(*new WebAuthenticationPanel(rpId));
+    return adoptRef(*new WebAuthenticationPanel(manager, rpId));
 }
 
-WebAuthenticationPanel::WebAuthenticationPanel(const String& rpId)
-    : m_rpId(rpId)
+WebAuthenticationPanel::WebAuthenticationPanel(const AuthenticatorManager& manager, const String& rpId)
+    : m_manager(makeWeakPtr(manager))
+    , m_rpId(rpId)
     , m_client(WTF::makeUniqueRef<WebAuthenticationPanelClient>())
 {
 }
 
 WebAuthenticationPanel::~WebAuthenticationPanel() = default;
 
+void WebAuthenticationPanel::cancel() const
+{
+    if (m_manager)
+        m_manager->cancelRequest(*this);
+}
+
 void WebAuthenticationPanel::setClient(UniqueRef<WebAuthenticationPanelClient>&& client)
 {
     m_client = WTFMove(client);
index 7504886..78e7ee9 100644 (file)
 #include <wtf/WeakPtr.h>
 #include <wtf/text/WTFString.h>
 
+namespace WebKit {
+class AuthenticatorManager;
+}
+
 namespace API {
 
 class WebAuthenticationPanelClient;
 
 class WebAuthenticationPanel final : public ObjectImpl<Object::Type::WebAuthenticationPanel>, public CanMakeWeakPtr<WebAuthenticationPanel> {
 public:
-    static Ref<WebAuthenticationPanel> create(const String& rpId);
+    static Ref<WebAuthenticationPanel> create(const WebKit::AuthenticatorManager&, const String& rpId);
     ~WebAuthenticationPanel();
 
     WTF::String rpId() const { return m_rpId; }
+    void cancel() const;
 
     const WebAuthenticationPanelClient& client() const { return m_client.get(); }
     void setClient(UniqueRef<WebAuthenticationPanelClient>&&);
 
 private:
-    WebAuthenticationPanel(const String& rpId);
+    WebAuthenticationPanel(const WebKit::AuthenticatorManager&, const String& rpId);
 
+    WeakPtr<WebKit::AuthenticatorManager> m_manager;
     WTF::String m_rpId;
     UniqueRef<WebAuthenticationPanelClient> m_client;
 };
index 1c41a3c..d032880 100644 (file)
@@ -164,6 +164,30 @@ void AuthenticatorManager::handleRequest(WebAuthenticationRequestData&& data, Ca
     runPanel();
 }
 
+void AuthenticatorManager::cancelRequest(const WebCore::PageIdentifier& pageID, const Optional<FrameIdentifier>& frameID)
+{
+    if (!m_pendingCompletionHandler)
+        return;
+    ASSERT(m_pendingRequestData.frameID);
+    if (m_pendingRequestData.frameID->pageID != pageID)
+        return;
+    if (frameID && frameID != m_pendingRequestData.frameID->frameID)
+        return;
+    invokePendingCompletionHandler(ExceptionData { NotAllowedError, "Operation timed out."_s });
+    clearState();
+    m_requestTimeOutTimer.stop();
+}
+
+// The following implements part of Step 20. of https://www.w3.org/TR/webauthn/#createCredential
+// and part of Step 18. of https://www.w3.org/TR/webauthn/#getAssertion as of 4 March 2019:
+// "If the user exercises a user agent user-interface option to cancel the process,".
+void AuthenticatorManager::cancelRequest(const API::WebAuthenticationPanel& panel)
+{
+    if (!m_pendingCompletionHandler || m_pendingRequestData.panel.get() != &panel)
+        return;
+    resetState();
+}
+
 void AuthenticatorManager::clearStateAsync()
 {
     RunLoop::main().dispatch([weakThis = makeWeakPtr(*this)] {
@@ -177,9 +201,7 @@ void AuthenticatorManager::clearState()
 {
     if (m_pendingCompletionHandler)
         return;
-    m_pendingRequestData = { };
-    m_services.clear();
-    m_authenticators.clear();
+    resetState();
 }
 
 void AuthenticatorManager::authenticatorAdded(Ref<Authenticator>&& authenticator)
@@ -264,7 +286,7 @@ void AuthenticatorManager::runPanel()
     if (!page)
         return;
 
-    m_pendingRequestData.panel = API::WebAuthenticationPanel::create(getRpId(m_pendingRequestData.options));
+    m_pendingRequestData.panel = API::WebAuthenticationPanel::create(*this, getRpId(m_pendingRequestData.options));
     auto& panel = *m_pendingRequestData.panel;
     page->uiClient().runWebAuthenticationPanel(*page, panel, [weakPanel = makeWeakPtr(panel), weakThis = makeWeakPtr(*this), this] (WebAuthenticationPanelResult result) {
         // The panel address is used to determine if the current pending request is still the same.
@@ -305,6 +327,14 @@ void AuthenticatorManager::invokePendingCompletionHandler(Respond&& respond)
     m_pendingCompletionHandler(WTFMove(respond));
 }
 
+
+void AuthenticatorManager::resetState()
+{
+    m_authenticators.clear();
+    m_services.clear();
+    m_pendingRequestData = { };
+}
+
 } // namespace WebKit
 
 #endif // ENABLE(WEB_AUTHN)
index d3766ad..5026267 100644 (file)
 #include <wtf/RunLoop.h>
 #include <wtf/Vector.h>
 
+namespace API {
+class WebAuthenticationPanel;
+}
+
 namespace WebKit {
 
 class AuthenticatorManager : public AuthenticatorTransportService::Observer, public Authenticator::Observer {
@@ -55,6 +59,8 @@ public:
     virtual ~AuthenticatorManager() = default;
 
     void handleRequest(WebAuthenticationRequestData&&, Callback&&);
+    void cancelRequest(const WebCore::PageIdentifier&, const Optional<WebCore::FrameIdentifier>&); // Called from WebPageProxy/WebProcessProxy.
+    void cancelRequest(const API::WebAuthenticationPanel&); // Called from panel clients.
 
     virtual bool isMock() const { return false; }
 
@@ -82,10 +88,11 @@ private:
     void timeOutTimerFired();
     void runPanel();
     void startRequest();
+    void resetState();
 
     // Request: We only allow one request per time. A new request will cancel any pending ones.
     WebAuthenticationRequestData m_pendingRequestData;
-    Callback m_pendingCompletionHandler; // Should be invoked directly, use invokePendingCompletionHandler.
+    Callback m_pendingCompletionHandler; // Should not be invoked directly, use invokePendingCompletionHandler.
     RunLoop::Timer<AuthenticatorManager> m_requestTimeOutTimer;
 
     Vector<UniqueRef<AuthenticatorTransportService>> m_services;
index 4eab5dd..e187a24 100644 (file)
@@ -55,6 +55,7 @@ public:
     // Overrided by MockHidConnection.
     virtual void initialize();
     virtual void terminate();
+    virtual DataSent sendSync(const Vector<uint8_t>& data);
     // Caller should send data again after callback is invoked to control flow.
     virtual void send(Vector<uint8_t>&& data, DataSentCallback&&);
     void registerDataReceivedCallback(DataReceivedCallback&&);
@@ -64,8 +65,8 @@ public:
     void receiveReport(Vector<uint8_t>&&);
 
 protected:
-    bool m_initialized { false };
-    bool m_terminated { false };
+    bool isInitialized() const { return m_isInitialized; }
+    void setIsInitialized(bool isInitialized) { m_isInitialized = isInitialized; }
 
 private:
     void consumeReports();
@@ -78,6 +79,7 @@ private:
     // Could queue data requested by other applications.
     Deque<Vector<uint8_t>> m_inputReports;
     DataReceivedCallback m_inputCallback;
+    bool m_isInitialized { false };
 };
 
 } // namespace WebKit
index f077ae9..f2f193c 100644 (file)
@@ -63,7 +63,7 @@ HidConnection::HidConnection(IOHIDDeviceRef device)
 
 HidConnection::~HidConnection()
 {
-    ASSERT(m_terminated);
+    ASSERT(!m_isInitialized);
 }
 
 void HidConnection::initialize()
@@ -72,7 +72,7 @@ void HidConnection::initialize()
     IOHIDDeviceScheduleWithRunLoop(m_device.get(), CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
     m_inputBuffer.resize(kHidMaxPacketSize);
     IOHIDDeviceRegisterInputReportCallback(m_device.get(), m_inputBuffer.data(), m_inputBuffer.size(), &reportReceived, this);
-    m_initialized = true;
+    m_isInitialized = true;
 }
 
 void HidConnection::terminate()
@@ -80,12 +80,23 @@ void HidConnection::terminate()
     IOHIDDeviceRegisterInputReportCallback(m_device.get(), m_inputBuffer.data(), m_inputBuffer.size(), nullptr, nullptr);
     IOHIDDeviceUnscheduleFromRunLoop(m_device.get(), CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
     IOHIDDeviceClose(m_device.get(), kIOHIDOptionsTypeNone);
-    m_terminated = true;
+    m_isInitialized = false;
+}
+
+auto HidConnection::sendSync(const Vector<uint8_t>& data) -> DataSent
+{
+    ASSERT(m_isInitialized);
+    auto status = IOHIDDeviceSetReport(m_device.get(), kIOHIDReportTypeOutput, kHidReportId, data.data(), data.size());
+    if (status) {
+        LOG_ERROR("Couldn't send report to the authenticator: %d", status);
+        return DataSent::No;
+    }
+    return DataSent::Yes;
 }
 
 void HidConnection::send(Vector<uint8_t>&& data, DataSentCallback&& callback)
 {
-    ASSERT(m_initialized);
+    ASSERT(m_isInitialized);
     auto task = makeBlockPtr([device = m_device, data = WTFMove(data), callback = WTFMove(callback)]() mutable {
         ASSERT(!RunLoop::isMain());
 
@@ -104,7 +115,7 @@ void HidConnection::send(Vector<uint8_t>&& data, DataSentCallback&& callback)
 
 void HidConnection::registerDataReceivedCallback(DataReceivedCallback&& callback)
 {
-    ASSERT(m_initialized);
+    ASSERT(m_isInitialized);
     ASSERT(!m_inputCallback);
     m_inputCallback = WTFMove(callback);
     consumeReports();
index 658466d..9e5d8fa 100644 (file)
@@ -60,17 +60,29 @@ MockHidConnection::MockHidConnection(IOHIDDeviceRef device, const MockWebAuthent
 
 void MockHidConnection::initialize()
 {
-    m_initialized = true;
+    setIsInitialized(true);
 }
 
 void MockHidConnection::terminate()
 {
-    m_terminated = true;
+    setIsInitialized(false);
+}
+
+auto MockHidConnection::sendSync(const Vector<uint8_t>& data) -> DataSent
+{
+    ASSERT(isInitialized());
+    if (m_configuration.hid->expectCancel) {
+        auto message = FidoHidMessage::createFromSerializedData(data);
+        ASSERT_UNUSED(message, message);
+        ASSERT(message->cmd() == FidoHidDeviceCommand::kCancel);
+        LOG_ERROR("Request cancelled.");
+    }
+    return DataSent::Yes;
 }
 
 void MockHidConnection::send(Vector<uint8_t>&& data, DataSentCallback&& callback)
 {
-    ASSERT(m_initialized);
+    ASSERT(isInitialized());
     auto task = makeBlockPtr([weakThis = makeWeakPtr(*this), data = WTFMove(data), callback = WTFMove(callback)]() mutable {
         ASSERT(!RunLoop::isMain());
         RunLoop::main().dispatch([weakThis, data = WTFMove(data), callback = WTFMove(callback)]() mutable {
@@ -228,6 +240,8 @@ void MockHidConnection::feedReports()
     }
 
     if (m_stage == Mock::HidStage::Request && m_subStage == Mock::HidSubStage::Msg) {
+        if (m_configuration.hid->expectCancel)
+            return;
         if (m_configuration.hid->keepAlive) {
             m_configuration.hid->keepAlive = false;
             FidoHidInitPacket initPacket(m_currentChannel, FidoHidDeviceCommand::kKeepAlive, { CtapKeepAliveStatusProcessing }, 1);
index 0db00e9..88b284f 100644 (file)
@@ -48,9 +48,11 @@ public:
     MockHidConnection(IOHIDDeviceRef, const WebCore::MockWebAuthenticationConfiguration&);
 
 private:
-    void send(Vector<uint8_t>&& data, DataSentCallback&&) final;
+    // HidConnection
     void initialize() final;
     void terminate() final;
+    DataSent sendSync(const Vector<uint8_t>& data) final;
+    void send(Vector<uint8_t>&& data, DataSentCallback&&) final;
     void registerDataReceivedCallbackInternal() final;
 
     void assembleRequest(Vector<uint8_t>&&);
index 4bb5485..9786054 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "APIWebAuthenticationPanel.h"
 #include "WebAuthenticationPanelFlags.h"
+#include <WebCore/GlobalFrameIdentifier.h>
 #include <WebCore/PublicKeyCredentialCreationOptions.h>
 #include <WebCore/PublicKeyCredentialRequestOptions.h>
 #include <wtf/Variant.h>
@@ -45,6 +46,7 @@ struct WebAuthenticationRequestData {
     WeakPtr<WebPageProxy> page;
     WebAuthenticationPanelResult panelResult { WebAuthenticationPanelResult::Unavailable };
     RefPtr<API::WebAuthenticationPanel> panel;
+    WTF::Optional<WebCore::GlobalFrameIdentifier> frameID;
 };
 
 } // namespace WebKit
index 4fcd8cd..6df8ca7 100644 (file)
@@ -31,7 +31,6 @@
 #include "AuthenticatorManager.h"
 #include "LocalService.h"
 #include "WebAuthenticationPanelFlags.h"
-#include "WebAuthenticatorCoordinatorMessages.h"
 #include "WebAuthenticatorCoordinatorProxyMessages.h"
 #include "WebPageProxy.h"
 #include "WebProcessProxy.h"
@@ -54,40 +53,32 @@ WebAuthenticatorCoordinatorProxy::~WebAuthenticatorCoordinatorProxy()
     m_webPageProxy.process().removeMessageReceiver(Messages::WebAuthenticatorCoordinatorProxy::messageReceiverName(), m_webPageProxy.webPageID());
 }
 
-void WebAuthenticatorCoordinatorProxy::makeCredential(uint64_t messageId, const Vector<uint8_t>& hash, const WebCore::PublicKeyCredentialCreationOptions& options)
+void WebAuthenticatorCoordinatorProxy::makeCredential(FrameIdentifier frameId, const Vector<uint8_t>& hash, const WebCore::PublicKeyCredentialCreationOptions& options, RequestCompletionHandler&& handler)
 {
-    handleRequest(messageId, { hash, options, makeWeakPtr(m_webPageProxy), WebAuthenticationPanelResult::Unavailable, nullptr });
+    handleRequest({ hash, options, makeWeakPtr(m_webPageProxy), WebAuthenticationPanelResult::Unavailable, nullptr, GlobalFrameIdentifier { m_webPageProxy.webPageID(), frameId } }, WTFMove(handler));
 }
 
-void WebAuthenticatorCoordinatorProxy::getAssertion(uint64_t messageId, const Vector<uint8_t>& hash, const WebCore::PublicKeyCredentialRequestOptions& options)
+void WebAuthenticatorCoordinatorProxy::getAssertion(FrameIdentifier frameId, const Vector<uint8_t>& hash, const WebCore::PublicKeyCredentialRequestOptions& options, RequestCompletionHandler&& handler)
 {
-    handleRequest(messageId, { hash, options, makeWeakPtr(m_webPageProxy), WebAuthenticationPanelResult::Unavailable, nullptr });
+    handleRequest({ hash, options, makeWeakPtr(m_webPageProxy), WebAuthenticationPanelResult::Unavailable, nullptr, GlobalFrameIdentifier { m_webPageProxy.webPageID(), frameId } }, WTFMove(handler));
 }
 
-void WebAuthenticatorCoordinatorProxy::handleRequest(uint64_t messageId, WebAuthenticationRequestData&& data)
+void WebAuthenticatorCoordinatorProxy::handleRequest(WebAuthenticationRequestData&& data, RequestCompletionHandler&& handler)
 {
-    auto callback = [messageId, weakThis = makeWeakPtr(*this)] (Variant<WebCore::PublicKeyCredentialData, WebCore::ExceptionData>&& result) {
+    auto callback = [handler = WTFMove(handler)] (Variant<WebCore::PublicKeyCredentialData, WebCore::ExceptionData>&& result) mutable {
         ASSERT(RunLoop::isMain());
-        if (!weakThis)
-            return;
-
         WTF::switchOn(result, [&](const WebCore::PublicKeyCredentialData& data) {
-            weakThis->requestReply(messageId, data, { });
+            handler(data, { });
         }, [&](const  WebCore::ExceptionData& exception) {
-            weakThis->requestReply(messageId, { }, exception);
+            handler({ }, exception);
         });
     };
     m_webPageProxy.websiteDataStore().authenticatorManager().handleRequest(WTFMove(data), WTFMove(callback));
 }
 
-void WebAuthenticatorCoordinatorProxy::isUserVerifyingPlatformAuthenticatorAvailable(uint64_t messageId)
-{
-    m_webPageProxy.send(Messages::WebAuthenticatorCoordinator::IsUserVerifyingPlatformAuthenticatorAvailableReply(messageId, LocalService::isAvailable()));
-}
-
-void WebAuthenticatorCoordinatorProxy::requestReply(uint64_t messageId, const WebCore::PublicKeyCredentialData& data, const WebCore::ExceptionData& exception)
+void WebAuthenticatorCoordinatorProxy::isUserVerifyingPlatformAuthenticatorAvailable(QueryCompletionHandler&& handler)
 {
-    m_webPageProxy.send(Messages::WebAuthenticatorCoordinator::RequestReply(messageId, data, exception));
+    handler(LocalService::isAvailable());
 }
 
 } // namespace WebKit
index ef73341..a1b257e 100644 (file)
@@ -28,9 +28,9 @@
 #if ENABLE(WEB_AUTHN)
 
 #include "MessageReceiver.h"
+#include <WebCore/FrameIdentifier.h>
 #include <wtf/Forward.h>
 #include <wtf/Noncopyable.h>
-#include <wtf/WeakPtr.h>
 
 namespace WebCore {
 struct ExceptionData;
@@ -45,7 +45,7 @@ class WebPageProxy;
 
 struct WebAuthenticationRequestData;
 
-class WebAuthenticatorCoordinatorProxy : private IPC::MessageReceiver, public CanMakeWeakPtr<WebAuthenticatorCoordinatorProxy> {
+class WebAuthenticatorCoordinatorProxy : private IPC::MessageReceiver {
     WTF_MAKE_FAST_ALLOCATED;
     WTF_MAKE_NONCOPYABLE(WebAuthenticatorCoordinatorProxy);
 public:
@@ -53,18 +53,18 @@ public:
     ~WebAuthenticatorCoordinatorProxy();
 
 private:
+    using RequestCompletionHandler = CompletionHandler<void(const WebCore::PublicKeyCredentialData&, const WebCore::ExceptionData&)>;
+    using QueryCompletionHandler = CompletionHandler<void(bool)>;
+
     // IPC::MessageReceiver.
     void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override;
 
     // Receivers.
-    void makeCredential(uint64_t messageId, const Vector<uint8_t>& hash, const WebCore::PublicKeyCredentialCreationOptions&);
-    void getAssertion(uint64_t messageId, const Vector<uint8_t>& hash, const WebCore::PublicKeyCredentialRequestOptions&);
-    void isUserVerifyingPlatformAuthenticatorAvailable(uint64_t messageId);
-
-    // Senders.
-    void requestReply(uint64_t messageId, const WebCore::PublicKeyCredentialData&, const WebCore::ExceptionData&);
+    void makeCredential(WebCore::FrameIdentifier, const Vector<uint8_t>& hash, const WebCore::PublicKeyCredentialCreationOptions&, RequestCompletionHandler&&);
+    void getAssertion(WebCore::FrameIdentifier, const Vector<uint8_t>& hash, const WebCore::PublicKeyCredentialRequestOptions&, RequestCompletionHandler&&);
+    void isUserVerifyingPlatformAuthenticatorAvailable(QueryCompletionHandler&&);
 
-    void handleRequest(uint64_t messageId, WebAuthenticationRequestData&&);
+    void handleRequest(WebAuthenticationRequestData&&, RequestCompletionHandler&&);
 
     WebPageProxy& m_webPageProxy;
 };
index 117cd88..caaffa7 100644 (file)
@@ -26,9 +26,9 @@
 
 messages -> WebAuthenticatorCoordinatorProxy {
 
-    MakeCredential(uint64_t messageId, Vector<uint8_t> hash, struct WebCore::PublicKeyCredentialCreationOptions options);
-    GetAssertion(uint64_t messageId, Vector<uint8_t> hash, struct WebCore::PublicKeyCredentialRequestOptions options);
-    IsUserVerifyingPlatformAuthenticatorAvailable(uint64_t messageId);
+    MakeCredential(WebCore::FrameIdentifier frameID, Vector<uint8_t> hash, struct WebCore::PublicKeyCredentialCreationOptions options) -> (struct WebCore::PublicKeyCredentialData data, struct WebCore::ExceptionData exception) Async
+    GetAssertion(WebCore::FrameIdentifier frameID, Vector<uint8_t> hash, struct WebCore::PublicKeyCredentialRequestOptions options) -> (struct WebCore::PublicKeyCredentialData data, struct WebCore::ExceptionData exception) Async
+    IsUserVerifyingPlatformAuthenticatorAvailable() -> (bool result) Async
 }
 
 #endif
index 8eda11b..ace4c0c 100644 (file)
@@ -42,11 +42,9 @@ using namespace WebCore;
 using namespace fido;
 
 CtapAuthenticator::CtapAuthenticator(std::unique_ptr<CtapDriver>&& driver, AuthenticatorGetInfoResponse&& info)
-    : m_driver(WTFMove(driver))
+    : FidoAuthenticator(WTFMove(driver))
     , m_info(WTFMove(info))
 {
-    // FIXME(191520): We need a way to convert std::unique_ptr to UniqueRef.
-    ASSERT(m_driver);
 }
 
 void CtapAuthenticator::makeCredential()
@@ -55,7 +53,7 @@ void CtapAuthenticator::makeCredential()
     if (processGoogleLegacyAppIdSupportExtension())
         return;
     auto cborCmd = encodeMakeCredenitalRequestAsCBOR(requestData().hash, WTF::get<PublicKeyCredentialCreationOptions>(requestData().options), m_info.options().userVerificationAvailability());
-    m_driver->transact(WTFMove(cborCmd), [weakThis = makeWeakPtr(*this)](Vector<uint8_t>&& data) {
+    driver().transact(WTFMove(cborCmd), [weakThis = makeWeakPtr(*this)](Vector<uint8_t>&& data) {
         ASSERT(RunLoop::isMain());
         if (!weakThis)
             return;
@@ -81,7 +79,7 @@ void CtapAuthenticator::getAssertion()
 {
     ASSERT(!m_isDowngraded);
     auto cborCmd = encodeGetAssertionRequestAsCBOR(requestData().hash, WTF::get<PublicKeyCredentialRequestOptions>(requestData().options), m_info.options().userVerificationAvailability());
-    m_driver->transact(WTFMove(cborCmd), [weakThis = makeWeakPtr(*this)](Vector<uint8_t>&& data) {
+    driver().transact(WTFMove(cborCmd), [weakThis = makeWeakPtr(*this)](Vector<uint8_t>&& data) {
         ASSERT(RunLoop::isMain());
         if (!weakThis)
             return;
@@ -110,8 +108,8 @@ bool CtapAuthenticator::tryDowngrade()
         return false;
 
     m_isDowngraded = true;
-    m_driver->setProtocol(ProtocolVersion::kU2f);
-    observer()->downgrade(this, U2fAuthenticator::create(WTFMove(m_driver)));
+    driver().setProtocol(ProtocolVersion::kU2f);
+    observer()->downgrade(this, U2fAuthenticator::create(releaseDriver()));
     return true;
 }
 
index 8054198..ee543d8 100644 (file)
 
 #if ENABLE(WEB_AUTHN)
 
-#include "Authenticator.h"
+#include "FidoAuthenticator.h"
 #include <WebCore/AuthenticatorGetInfoResponse.h>
 
 namespace WebKit {
 
 class CtapDriver;
 
-class CtapAuthenticator final : public Authenticator {
+class CtapAuthenticator final : public FidoAuthenticator {
 public:
     static Ref<CtapAuthenticator> create(std::unique_ptr<CtapDriver>&& driver, fido::AuthenticatorGetInfoResponse&& info)
     {
@@ -52,7 +52,6 @@ private:
     bool tryDowngrade();
     bool processGoogleLegacyAppIdSupportExtension();
 
-    std::unique_ptr<CtapDriver> m_driver;
     fido::AuthenticatorGetInfoResponse m_info;
     bool m_isDowngraded { false };
 };
index 079bea3..7a420fe 100644 (file)
@@ -41,14 +41,15 @@ class CtapDriver : public CanMakeWeakPtr<CtapDriver> {
 public:
     using ResponseCallback = Function<void(Vector<uint8_t>&&)>;
 
-    CtapDriver() = default;
     virtual ~CtapDriver() = default;
 
     void setProtocol(fido::ProtocolVersion protocol) { m_protocol = protocol; }
 
     virtual void transact(Vector<uint8_t>&& data, ResponseCallback&&) = 0;
+    virtual void cancel() { };
 
 protected:
+    CtapDriver() = default;
     fido::ProtocolVersion protocol() const { return m_protocol; }
 
 private:
index 5326581..06f2315 100644 (file)
@@ -68,9 +68,11 @@ void CtapHidDriver::Worker::transact(fido::FidoHidMessage&& requestMessage, Mess
 
 void CtapHidDriver::Worker::write(HidConnection::DataSent sent)
 {
-    ASSERT(m_state == State::Write);
+    if (m_state != State::Write)
+        return;
     if (sent != HidConnection::DataSent::Yes) {
-        returnMessage(WTF::nullopt);
+        m_responseMessage = WTF::nullopt;
+        returnMessage();
         return;
     }
 
@@ -95,7 +97,8 @@ void CtapHidDriver::Worker::write(HidConnection::DataSent sent)
 
 void CtapHidDriver::Worker::read(const Vector<uint8_t>& data)
 {
-    ASSERT(m_state == State::Read);
+    if (m_state != State::Read)
+        return;
     if (!m_responseMessage) {
         m_responseMessage = FidoHidMessage::createFromSerializedData(data);
         // The first few reports could be for other applications, and therefore ignore those.
@@ -107,7 +110,8 @@ void CtapHidDriver::Worker::read(const Vector<uint8_t>& data)
     } else {
         if (!m_responseMessage->addContinuationPacket(data)) {
             LOG_ERROR("Couldn't parse a hid continuation packet.");
-            returnMessage(WTF::nullopt);
+            m_responseMessage = WTF::nullopt;
+            returnMessage();
             return;
         }
     }
@@ -119,16 +123,37 @@ void CtapHidDriver::Worker::read(const Vector<uint8_t>& data)
             m_responseMessage.reset();
             return;
         }
-        returnMessage(WTFMove(m_responseMessage));
+        returnMessage();
         return;
     }
 }
 
-void CtapHidDriver::Worker::returnMessage(Optional<fido::FidoHidMessage>&& message)
+void CtapHidDriver::Worker::returnMessage()
+{
+    // Reset state before calling the response callback to avoid being deleted.
+    auto callback = WTFMove(m_callback);
+    auto message = WTFMove(m_responseMessage);
+    reset();
+    callback(WTFMove(message));
+}
+
+void CtapHidDriver::Worker::reset()
 {
-    m_state = State::Idle;
     m_connection->unregisterDataReceivedCallback();
-    m_callback(WTFMove(message));
+    m_callback = nullptr;
+    m_responseMessage = WTF::nullopt;
+    m_requestMessage = WTF::nullopt;
+    m_state = State::Idle;
+}
+
+// This implements CTAPHID_CANCEL which violates the transaction semantics:
+// https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#usb-hid-cancel
+void CtapHidDriver::Worker::cancel(fido::FidoHidMessage&& requestMessage)
+{
+    reset();
+    m_connection->invalidateCache();
+    ASSERT(requestMessage.numPackets() == 1);
+    m_connection->sendSync(requestMessage.popNextPacket());
 }
 
 CtapHidDriver::CtapHidDriver(UniqueRef<HidConnection>&& connection)
@@ -167,7 +192,8 @@ void CtapHidDriver::transact(Vector<uint8_t>&& data, ResponseCallback&& callback
 
 void CtapHidDriver::continueAfterChannelAllocated(Optional<FidoHidMessage>&& message)
 {
-    ASSERT(m_state == State::AllocateChannel);
+    if (m_state != State::AllocateChannel)
+        return;
     if (!message) {
         returnResponse({ });
         return;
@@ -193,7 +219,7 @@ void CtapHidDriver::continueAfterChannelAllocated(Optional<FidoHidMessage>&& mes
     m_channelId |= static_cast<uint32_t>(payload[index++]) << 16;
     m_channelId |= static_cast<uint32_t>(payload[index++]) << 8;
     m_channelId |= static_cast<uint32_t>(payload[index]);
-    // FIXME(191534): Check the reset of the payload.
+    // FIXME(191534): Check the rest of the payload.
     auto cmd = FidoHidMessage::create(m_channelId, protocol() == ProtocolVersion::kCtap ? FidoHidDeviceCommand::kCbor : FidoHidDeviceCommand::kMsg, m_requestData);
     ASSERT(cmd);
     m_worker->transact(WTFMove(*cmd), [weakThis = makeWeakPtr(*this)](Optional<FidoHidMessage>&& response) mutable {
@@ -206,7 +232,8 @@ void CtapHidDriver::continueAfterChannelAllocated(Optional<FidoHidMessage>&& mes
 
 void CtapHidDriver::continueAfterResponseReceived(Optional<fido::FidoHidMessage>&& message)
 {
-    ASSERT(m_state == State::Ready);
+    if (m_state != State::Ready)
+        return;
     ASSERT(!message || message->channelId() == m_channelId);
     returnResponse(message ? message->getMessagePayload() : Vector<uint8_t>());
 }
@@ -214,8 +241,28 @@ void CtapHidDriver::continueAfterResponseReceived(Optional<fido::FidoHidMessage>
 void CtapHidDriver::returnResponse(Vector<uint8_t>&& response)
 {
     // Reset state before calling the response callback to avoid being deleted.
+    auto responseCallback = WTFMove(m_responseCallback);
+    reset();
+    responseCallback(WTFMove(response));
+}
+
+void CtapHidDriver::reset()
+{
+    m_responseCallback = nullptr;
+    m_channelId = fido::kHidBroadcastChannel;
     m_state = State::Idle;
-    m_responseCallback(WTFMove(response));
+}
+
+void CtapHidDriver::cancel()
+{
+    if (m_state == State::Idle || protocol() != ProtocolVersion::kCtap)
+        return;
+    // Cancel any outstanding requests.
+    if (m_state == State::Ready) {
+        auto cancelCommand = FidoHidMessage::create(m_channelId, FidoHidDeviceCommand::kCancel, { });
+        m_worker->cancel(WTFMove(*cancelCommand));
+    }
+    reset();
 }
 
 } // namespace WebKit
index c5d6dca..bfb213d 100644 (file)
@@ -37,7 +37,7 @@ namespace WebKit {
 // The following implements the CTAP HID protocol:
 // https://fidoalliance.org/specs/fido-v2.0-ps-20170927/fido-client-to-authenticator-protocol-v2.0-ps-20170927.html#usb
 // FSM: Idle => AllocateChannel => Ready
-class CtapHidDriver : public CtapDriver {
+class CtapHidDriver final : public CtapDriver {
 public:
     enum class State : uint8_t {
         Idle,
@@ -50,6 +50,7 @@ public:
     explicit CtapHidDriver(UniqueRef<HidConnection>&&);
 
     void transact(Vector<uint8_t>&& data, ResponseCallback&&) final;
+    void cancel() final;
 
 private:
     // Worker is the helper that maintains the transaction.
@@ -71,11 +72,13 @@ private:
         ~Worker();
 
         void transact(fido::FidoHidMessage&&, MessageCallback&&);
+        void cancel(fido::FidoHidMessage&&);
 
     private:
         void write(HidConnection::DataSent);
         void read(const Vector<uint8_t>&);
-        void returnMessage(Optional<fido::FidoHidMessage>&&);
+        void returnMessage();
+        void reset();
 
         UniqueRef<HidConnection> m_connection;
         State m_state { State::Idle };
@@ -87,6 +90,7 @@ private:
     void continueAfterChannelAllocated(Optional<fido::FidoHidMessage>&&);
     void continueAfterResponseReceived(Optional<fido::FidoHidMessage>&&);
     void returnResponse(Vector<uint8_t>&&);
+    void reset();
 
     UniqueRef<Worker> m_worker;
     State m_state { State::Idle };
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 Apple Inc. All rights reserved.
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  */
 
 #include "config.h"
-#include "AuthenticatorCoordinatorClient.h"
+#include "FidoAuthenticator.h"
 
 #if ENABLE(WEB_AUTHN)
 
-#include "PublicKeyCredentialData.h"
+namespace WebKit {
 
-namespace WebCore {
-
-void AuthenticatorCoordinatorClient::requestReply(uint64_t messageId, const WebCore::PublicKeyCredentialData& data, const WebCore::ExceptionData& exception)
+FidoAuthenticator::FidoAuthenticator(std::unique_ptr<CtapDriver>&& driver)
+    : m_driver(WTFMove(driver))
 {
-    if (messageId != m_accumulatedRequestMessageId - 1)
-        return;
-    m_pendingCompletionHandler(data, exception);
+    ASSERT(m_driver);
 }
 
-void AuthenticatorCoordinatorClient::isUserVerifyingPlatformAuthenticatorAvailableReply(uint64_t messageId, bool result)
+FidoAuthenticator::~FidoAuthenticator()
 {
-    auto handler = m_pendingQueryCompletionHandlers.take(messageId);
-    handler(result);
+    if (m_driver)
+        m_driver->cancel();
 }
 
-uint64_t AuthenticatorCoordinatorClient::setRequestCompletionHandler(RequestCompletionHandler&& handler)
+CtapDriver& FidoAuthenticator::driver() const
 {
-    if (m_pendingCompletionHandler)
-        m_pendingCompletionHandler({ }, { NotAllowedError, "This request has been voided by a new request."_s });
-
-    m_pendingCompletionHandler = WTFMove(handler);
-    return m_accumulatedRequestMessageId++;
+    ASSERT(m_driver);
+    return *m_driver;
 }
 
-uint64_t AuthenticatorCoordinatorClient::addQueryCompletionHandler(QueryCompletionHandler&& handler)
+std::unique_ptr<CtapDriver> FidoAuthenticator::releaseDriver()
 {
-    uint64_t messageId = m_accumulatedQueryMessageId++;
-    auto addResult = m_pendingQueryCompletionHandlers.add(messageId, WTFMove(handler));
-    ASSERT_UNUSED(addResult, addResult.isNewEntry);
-    return messageId;
+    ASSERT(m_driver);
+    return WTFMove(m_driver);
 }
 
-} // namespace WebCore
+} // namespace WebKit
 
 #endif // ENABLE(WEB_AUTHN)
diff --git a/Source/WebKit/UIProcess/WebAuthentication/fido/FidoAuthenticator.h b/Source/WebKit/UIProcess/WebAuthentication/fido/FidoAuthenticator.h
new file mode 100644 (file)
index 0000000..71566e4
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(WEB_AUTHN)
+
+#include "Authenticator.h"
+
+namespace WebKit {
+
+class CtapDriver;
+
+class FidoAuthenticator : public Authenticator {
+public:
+    ~FidoAuthenticator() override;
+
+protected:
+    explicit FidoAuthenticator(std::unique_ptr<CtapDriver>&&);
+
+    CtapDriver& driver() const;
+    std::unique_ptr<CtapDriver> releaseDriver();
+
+private:
+    std::unique_ptr<CtapDriver> m_driver;
+};
+
+} // namespace WebKit
+
+#endif // ENABLE(WEB_AUTHN)
index ea14ee0..1b5654b 100644 (file)
@@ -64,7 +64,7 @@ void FidoService::continueAfterGetInfo(WeakPtr<CtapDriver>&& weakDriver, Vector<
     if (!weakDriver)
         return;
 
-    std::unique_ptr<CtapDriver> driver = m_drivers.take(weakDriver.get());
+    auto driver = m_drivers.take(weakDriver.get());
     if (!driver || !observer() || response.isEmpty())
         return;
 
index bd42459..ab46536 100644 (file)
@@ -46,11 +46,9 @@ const unsigned retryTimeOutValueMs = 200;
 }
 
 U2fAuthenticator::U2fAuthenticator(std::unique_ptr<CtapDriver>&& driver)
-    : m_driver(WTFMove(driver))
+    : FidoAuthenticator(WTFMove(driver))
     , m_retryTimer(RunLoop::main(), this, &U2fAuthenticator::retryLastCommand)
 {
-    // FIXME(191520): We need a way to convert std::unique_ptr to UniqueRef.
-    ASSERT(m_driver);
 }
 
 void U2fAuthenticator::makeCredential()
@@ -118,7 +116,7 @@ void U2fAuthenticator::issueNewCommand(Vector<uint8_t>&& command, CommandType ty
 
 void U2fAuthenticator::issueCommand(const Vector<uint8_t>& command, CommandType type)
 {
-    m_driver->transact(Vector<uint8_t>(command), [weakThis = makeWeakPtr(*this), type](Vector<uint8_t>&& data) {
+    driver().transact(Vector<uint8_t>(command), [weakThis = makeWeakPtr(*this), type](Vector<uint8_t>&& data) {
         ASSERT(RunLoop::isMain());
         if (!weakThis)
             return;
index d9055cd..4672748 100644 (file)
@@ -27,7 +27,7 @@
 
 #if ENABLE(WEB_AUTHN)
 
-#include "Authenticator.h"
+#include "FidoAuthenticator.h"
 #include <wtf/RunLoop.h>
 
 namespace apdu {
@@ -38,7 +38,7 @@ namespace WebKit {
 
 class CtapDriver;
 
-class U2fAuthenticator final : public Authenticator {
+class U2fAuthenticator final : public FidoAuthenticator {
 public:
     static Ref<U2fAuthenticator> create(std::unique_ptr<CtapDriver>&& driver)
     {
@@ -69,7 +69,6 @@ private:
     void continueBogusCommandAfterResponseReceived(apdu::ApduResponse&&);
     void continueSignCommandAfterResponseReceived(apdu::ApduResponse&&);
 
-    std::unique_ptr<CtapDriver> m_driver;
     RunLoop::Timer<U2fAuthenticator> m_retryTimer;
     Vector<uint8_t> m_lastCommand;
     CommandType m_lastCommandType;
index 2cc9693..cebd11f 100644 (file)
@@ -54,6 +54,7 @@
 #include "APIWebsitePolicies.h"
 #include "AuthenticationChallengeProxy.h"
 #include "AuthenticationDecisionListener.h"
+#include "AuthenticatorManager.h"
 #include "DataReference.h"
 #include "DownloadProxy.h"
 #include "DrawingAreaMessages.h"
@@ -4113,6 +4114,10 @@ void WebPageProxy::didStartProvisionalLoadForFrameShared(Ref<WebProcessProxy>&&
         m_loaderClient->didStartProvisionalLoadForFrame(*this, *frame, navigation.get(), process->transformHandlesToObjects(userData.object()).get());
     else if (frame->isMainFrame())
         m_navigationClient->didStartProvisionalNavigation(*this, navigation.get(), process->transformHandlesToObjects(userData.object()).get());
+
+#if ENABLE(WEB_AUTHN)
+    m_websiteDataStore->authenticatorManager().cancelRequest(m_webPageID, frameID);
+#endif
 }
 
 void WebPageProxy::didExplicitOpenForFrame(FrameIdentifier frameID, URL&& url)
@@ -7196,6 +7201,10 @@ void WebPageProxy::resetState(ResetStateReason resetStateReason)
 #if ENABLE(SPEECH_SYNTHESIS)
     resetSpeechSynthesizer();
 #endif
+
+#if ENABLE(WEB_AUTHN)
+    m_websiteDataStore->authenticatorManager().cancelRequest(m_webPageID, WTF::nullopt);
+#endif
 }
 
 void WebPageProxy::resetStateAfterProcessExited(ProcessTerminationReason terminationReason)
index 5038dec..1a579ce 100644 (file)
@@ -29,6 +29,7 @@
 #include "APIFrameHandle.h"
 #include "APIPageGroupHandle.h"
 #include "APIPageHandle.h"
+#include "AuthenticatorManager.h"
 #include "DataReference.h"
 #include "DownloadProxyMap.h"
 #include "LoadParameters.h"
@@ -890,6 +891,12 @@ void WebProcessProxy::didDestroyFrame(FrameIdentifier frameID)
     // back to the UIProcess, then the frameDestroyed message will still be received because it
     // gets sent directly to the WebProcessProxy.
     ASSERT(WebFrameProxyMap::isValidKey(frameID));
+#if ENABLE(WEB_AUTHN)
+    if (auto* frame = webFrame(frameID)) {
+        if (auto* page = frame->page())
+            page->websiteDataStore().authenticatorManager().cancelRequest(page->webPageID(), frameID);
+    }
+#endif
     m_frameMap.remove(frameID);
 }
 
index f9952c1..08e152d 100644 (file)
                57B8264C230603C100B72EB0 /* MockNfcService.h in Headers */ = {isa = PBXBuildFile; fileRef = 57B8264A230603C100B72EB0 /* MockNfcService.h */; };
                57BBEA6D22BC0BFE00273995 /* SOAuthorizationLoadPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = 57BBEA6C22BC0BFE00273995 /* SOAuthorizationLoadPolicy.h */; };
                57C6244B234679A400383FE7 /* WebAuthenticationPanelFlags.h in Headers */ = {isa = PBXBuildFile; fileRef = 57C6244A234679A400383FE7 /* WebAuthenticationPanelFlags.h */; };
-               57DCED6E2142EE5E0016B847 /* WebAuthenticatorCoordinatorMessageReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 57DCED6B2142EAE20016B847 /* WebAuthenticatorCoordinatorMessageReceiver.cpp */; };
                57DCED6F2142EE630016B847 /* WebAuthenticatorCoordinatorMessages.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DCED6A2142EAE20016B847 /* WebAuthenticatorCoordinatorMessages.h */; };
                57DCED702142EE680016B847 /* WebAuthenticatorCoordinatorProxyMessageReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 57DCED6C2142EAF90016B847 /* WebAuthenticatorCoordinatorProxyMessageReceiver.cpp */; };
                57DCED712142EE6C0016B847 /* WebAuthenticatorCoordinatorProxyMessages.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DCED6D2142EAFA0016B847 /* WebAuthenticatorCoordinatorProxyMessages.h */; };
                57597EC021818BE20037F924 /* CtapHidDriver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CtapHidDriver.cpp; sourceTree = "<group>"; };
                5760828B2029854200116678 /* WebAuthenticatorCoordinator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WebAuthenticatorCoordinator.h; sourceTree = "<group>"; };
                5760828C2029854200116678 /* WebAuthenticatorCoordinator.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = WebAuthenticatorCoordinator.cpp; sourceTree = "<group>"; };
-               5760828D202987E600116678 /* WebAuthenticatorCoordinator.messages.in */ = {isa = PBXFileReference; lastKnownFileType = text; path = WebAuthenticatorCoordinator.messages.in; sourceTree = "<group>"; };
                57608295202BD8BA00116678 /* WebAuthenticatorCoordinatorProxy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WebAuthenticatorCoordinatorProxy.h; sourceTree = "<group>"; };
                57608296202BD8BA00116678 /* WebAuthenticatorCoordinatorProxy.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = WebAuthenticatorCoordinatorProxy.cpp; sourceTree = "<group>"; };
                57608299202BDAE200116678 /* WebAuthenticatorCoordinatorProxy.messages.in */ = {isa = PBXFileReference; lastKnownFileType = text; path = WebAuthenticatorCoordinatorProxy.messages.in; sourceTree = "<group>"; };
                57B8264723050C5100B72EB0 /* FidoService.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FidoService.cpp; sourceTree = "<group>"; };
                57B8264A230603C100B72EB0 /* MockNfcService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockNfcService.h; sourceTree = "<group>"; };
                57B8264B230603C100B72EB0 /* MockNfcService.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MockNfcService.mm; sourceTree = "<group>"; };
+               57B8E3D52355864A00D5C5D1 /* FidoAuthenticator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FidoAuthenticator.h; sourceTree = "<group>"; };
+               57B8E3D62355864A00D5C5D1 /* FidoAuthenticator.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FidoAuthenticator.cpp; sourceTree = "<group>"; };
                57BBEA6C22BC0BFE00273995 /* SOAuthorizationLoadPolicy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SOAuthorizationLoadPolicy.h; sourceTree = "<group>"; };
                57C6244A234679A400383FE7 /* WebAuthenticationPanelFlags.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WebAuthenticationPanelFlags.h; sourceTree = "<group>"; };
                57DCED6A2142EAE20016B847 /* WebAuthenticatorCoordinatorMessages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WebAuthenticatorCoordinatorMessages.h; path = DerivedSources/WebKit2/WebAuthenticatorCoordinatorMessages.h; sourceTree = BUILT_PRODUCTS_DIR; };
                                57597EB721811D9A0037F924 /* CtapHidDriver.h */,
                                570DAAC9230385FD00E8FC04 /* CtapNfcDriver.cpp */,
                                570DAAC8230385FD00E8FC04 /* CtapNfcDriver.h */,
+                               57B8E3D62355864A00D5C5D1 /* FidoAuthenticator.cpp */,
+                               57B8E3D52355864A00D5C5D1 /* FidoAuthenticator.h */,
                                57B8264723050C5100B72EB0 /* FidoService.cpp */,
                                57B8264623050C5100B72EB0 /* FidoService.h */,
                                57EB2E3921E1983E00B89CDF /* U2fAuthenticator.cpp */,
                        children = (
                                5760828C2029854200116678 /* WebAuthenticatorCoordinator.cpp */,
                                5760828B2029854200116678 /* WebAuthenticatorCoordinator.h */,
-                               5760828D202987E600116678 /* WebAuthenticatorCoordinator.messages.in */,
                        );
                        path = WebAuthentication;
                        sourceTree = "<group>";
                                2684055218B86ED60022C38B /* ViewUpdateDispatcherMessageReceiver.cpp in Sources */,
                                1A60224C18C16B9F00C3E8C9 /* VisitedLinkStoreMessageReceiver.cpp in Sources */,
                                1A8E7D3C18C15149005A702A /* VisitedLinkTableControllerMessageReceiver.cpp in Sources */,
-                               57DCED6E2142EE5E0016B847 /* WebAuthenticatorCoordinatorMessageReceiver.cpp in Sources */,
                                57DCED702142EE680016B847 /* WebAuthenticatorCoordinatorProxyMessageReceiver.cpp in Sources */,
                                1C0A19571C90068F00FE0EBB /* WebAutomationSessionMessageReceiver.cpp in Sources */,
                                1C0A19531C8FFDFB00FE0EBB /* WebAutomationSessionProxyMessageReceiver.cpp in Sources */,
index 66fa1a7..8a9873b 100644 (file)
 
 #if ENABLE(WEB_AUTHN)
 
-#include "WebAuthenticatorCoordinatorMessages.h"
 #include "WebAuthenticatorCoordinatorProxyMessages.h"
+#include "WebFrame.h"
 #include "WebPage.h"
-#include "WebProcess.h"
 #include <WebCore/PublicKeyCredentialCreationOptions.h>
+#include <WebCore/PublicKeyCredentialData.h>
 #include <WebCore/PublicKeyCredentialRequestOptions.h>
 
 namespace WebKit {
+using namespace WebCore;
 
 WebAuthenticatorCoordinator::WebAuthenticatorCoordinator(WebPage& webPage)
     : m_webPage(webPage)
 {
-    WebProcess::singleton().addMessageReceiver(Messages::WebAuthenticatorCoordinator::messageReceiverName(), m_webPage.identifier(), *this);
 }
 
-WebAuthenticatorCoordinator::~WebAuthenticatorCoordinator()
+void WebAuthenticatorCoordinator::makeCredential(const Frame& frame, const Vector<uint8_t>& hash, const PublicKeyCredentialCreationOptions& options, RequestCompletionHandler&& handler)
 {
-    WebProcess::singleton().removeMessageReceiver(*this);
+    auto* webFrame = WebFrame::fromCoreFrame(frame);
+    if (!webFrame)
+        return;
+    m_webPage.sendWithAsyncReply(Messages::WebAuthenticatorCoordinatorProxy::MakeCredential(webFrame->frameID(), hash, options), WTFMove(handler));
 }
 
-void WebAuthenticatorCoordinator::makeCredential(const Vector<uint8_t>& hash, const WebCore::PublicKeyCredentialCreationOptions& options, WebCore::RequestCompletionHandler&& handler)
+void WebAuthenticatorCoordinator::getAssertion(const Frame& frame, const Vector<uint8_t>& hash, const PublicKeyCredentialRequestOptions& options, RequestCompletionHandler&& handler)
 {
-    auto messageId = setRequestCompletionHandler(WTFMove(handler));
-    m_webPage.send(Messages::WebAuthenticatorCoordinatorProxy::MakeCredential(messageId, hash, options));
+    auto* webFrame = WebFrame::fromCoreFrame(frame);
+    if (!webFrame)
+        return;
+    m_webPage.sendWithAsyncReply(Messages::WebAuthenticatorCoordinatorProxy::GetAssertion(webFrame->frameID(), hash, options), WTFMove(handler));
 }
 
-void WebAuthenticatorCoordinator::getAssertion(const Vector<uint8_t>& hash, const WebCore::PublicKeyCredentialRequestOptions& options, WebCore::RequestCompletionHandler&& handler)
+void WebAuthenticatorCoordinator::isUserVerifyingPlatformAuthenticatorAvailable(QueryCompletionHandler&& handler)
 {
-    auto messageId = setRequestCompletionHandler(WTFMove(handler));
-    m_webPage.send(Messages::WebAuthenticatorCoordinatorProxy::GetAssertion(messageId, hash, options));
-}
-
-void WebAuthenticatorCoordinator::isUserVerifyingPlatformAuthenticatorAvailable(WebCore::QueryCompletionHandler&& handler)
-{
-    auto messageId = addQueryCompletionHandler(WTFMove(handler));
-    m_webPage.send(Messages::WebAuthenticatorCoordinatorProxy::IsUserVerifyingPlatformAuthenticatorAvailable(messageId));
+    m_webPage.sendWithAsyncReply(Messages::WebAuthenticatorCoordinatorProxy::IsUserVerifyingPlatformAuthenticatorAvailable(), WTFMove(handler));
 }
 
 } // namespace WebKit
index 6bbc6b6..787e9a9 100644 (file)
 
 #if ENABLE(WEB_AUTHN)
 
-#include "MessageReceiver.h"
 #include <WebCore/AuthenticatorCoordinatorClient.h>
 
 namespace WebKit {
 
 class WebPage;
 
-class WebAuthenticatorCoordinator final : public WebCore::AuthenticatorCoordinatorClient, private IPC::MessageReceiver {
+class WebAuthenticatorCoordinator final : public WebCore::AuthenticatorCoordinatorClient {
 public:
     explicit WebAuthenticatorCoordinator(WebPage&);
-    ~WebAuthenticatorCoordinator();
 
 private:
     // WebCore::AuthenticatorCoordinatorClient
-    // Senders.
-    void makeCredential(const Vector<uint8_t>&, const WebCore::PublicKeyCredentialCreationOptions&, WebCore::RequestCompletionHandler&&) final;
-    void getAssertion(const Vector<uint8_t>& hash, const WebCore::PublicKeyCredentialRequestOptions&, WebCore::RequestCompletionHandler&&) final;
+    void makeCredential(const WebCore::Frame&, const Vector<uint8_t>&, const WebCore::PublicKeyCredentialCreationOptions&, WebCore::RequestCompletionHandler&&) final;
+    void getAssertion(const WebCore::Frame&, const Vector<uint8_t>& hash, const WebCore::PublicKeyCredentialRequestOptions&, WebCore::RequestCompletionHandler&&) final;
     void isUserVerifyingPlatformAuthenticatorAvailable(WebCore::QueryCompletionHandler&&) final;
 
-    // IPC::MessageReceiver.
-    void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override;
-
     WebPage& m_webPage;
 };
 
diff --git a/Source/WebKit/WebProcess/WebAuthentication/WebAuthenticatorCoordinator.messages.in b/Source/WebKit/WebProcess/WebAuthentication/WebAuthenticatorCoordinator.messages.in
deleted file mode 100644 (file)
index 18700ca..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (C) 2018 Apple Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
-# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
-# THE POSSIBILITY OF SUCH DAMAGE.
-#/
-
-#if ENABLE(WEB_AUTHN)
-
-messages -> WebAuthenticatorCoordinator {
-
-    RequestReply(uint64_t messageId, struct WebCore::PublicKeyCredentialData data, struct WebCore::ExceptionData exception);
-    IsUserVerifyingPlatformAuthenticatorAvailableReply(uint64_t messageId, bool result);
-}
-
-#endif
index 1dfe39d..3f193ed 100644 (file)
@@ -176,7 +176,7 @@ WebPage* WebFrame::page() const
     return nullptr;
 }
 
-WebFrame* WebFrame::fromCoreFrame(Frame& frame)
+WebFrame* WebFrame::fromCoreFrame(const Frame& frame)
 {
     auto* webFrameLoaderClient = toWebFrameLoaderClient(frame.loader().client());
     if (!webFrameLoaderClient)
index 2f0cc9d..b329ac5 100644 (file)
@@ -73,7 +73,7 @@ public:
 
     WebPage* page() const;
 
-    static WebFrame* fromCoreFrame(WebCore::Frame&);
+    static WebFrame* fromCoreFrame(const WebCore::Frame&);
     WebCore::Frame* coreFrame() const { return m_coreFrame; }
 
     FrameInfoData info() const;
index d7346a0..7363a1a 100644 (file)
@@ -1,3 +1,22 @@
+2019-10-18  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthn] Implement AuthenticatorCancel
+        https://bugs.webkit.org/show_bug.cgi?id=191523
+        <rdar://problem/55920204>
+
+        Reviewed by Brent Fulgham.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebCore/FidoHidMessageTest.cpp:
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/Tests/WebKitCocoa/_WKWebAuthenticationPanel.mm:
+        (-[TestWebAuthenticationPanelUIDelegate webView:runWebAuthenticationPanel:initiatedByFrame:completionHandler:]):
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-hid-cancel.html: Copied from Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-hid.html.
+        * TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-hid.html:
+        * TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-nfc.html:
+        * TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion.html:
+
 2019-10-18  Brian Burg  <bburg@apple.com>
 
         Cleanup: Stop setting -ApplePersistenceIgnoreState when running debug Mac WebKit
index 945eddb..14887b7 100644 (file)
                57599E2B1F071AA000A3FB8C /* IndexedDBStructuredCloneBackwardCompatibilityWrite.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 57599E231F07192C00A3FB8C /* IndexedDBStructuredCloneBackwardCompatibilityWrite.html */; };
                57663DEA234EA66D00E85E09 /* web-authentication-get-assertion-nfc.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 57663DE9234EA60B00E85E09 /* web-authentication-get-assertion-nfc.html */; };
                57663DEC234F1F9300E85E09 /* web-authentication-get-assertion-hid.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 57663DEB234F1F8000E85E09 /* web-authentication-get-assertion-hid.html */; };
+               57663DF32357E48900E85E09 /* web-authentication-get-assertion-hid-cancel.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 57663DF22357E45D00E85E09 /* web-authentication-get-assertion-hid-cancel.html */; };
                5769C50B1D9B0002000847FB /* SerializedCryptoKeyWrap.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5769C50A1D9B0001000847FB /* SerializedCryptoKeyWrap.mm */; };
                5774AA6821FBBF7800AF2A1B /* TestSOAuthorization.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5774AA6721FBBF7800AF2A1B /* TestSOAuthorization.mm */; };
                5778D05622110A2600899E3B /* LoadWebArchive.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5778D05522110A2600899E3B /* LoadWebArchive.mm */; };
                                CDC8E4971BC6F10800594FEC /* video-without-audio.mp4 in Copy Resources */,
                                2EBD9D0A2134730D002DA758 /* video.html in Copy Resources */,
                                CD577799211CE0E4001B371E /* web-audio-only.html in Copy Resources */,
+                               57663DF32357E48900E85E09 /* web-authentication-get-assertion-hid-cancel.html in Copy Resources */,
                                57663DEC234F1F9300E85E09 /* web-authentication-get-assertion-hid.html in Copy Resources */,
                                57663DEA234EA66D00E85E09 /* web-authentication-get-assertion-nfc.html in Copy Resources */,
                                57C624502346C21E00383FE7 /* web-authentication-get-assertion.html in Copy Resources */,
                57599E261F07192C00A3FB8C /* IndexedDBStructuredCloneBackwardCompatibility.sqlite3-shm */ = {isa = PBXFileReference; lastKnownFileType = file; path = "IndexedDBStructuredCloneBackwardCompatibility.sqlite3-shm"; sourceTree = "<group>"; };
                57663DE9234EA60B00E85E09 /* web-authentication-get-assertion-nfc.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "web-authentication-get-assertion-nfc.html"; sourceTree = "<group>"; };
                57663DEB234F1F8000E85E09 /* web-authentication-get-assertion-hid.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "web-authentication-get-assertion-hid.html"; sourceTree = "<group>"; };
+               57663DF22357E45D00E85E09 /* web-authentication-get-assertion-hid-cancel.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "web-authentication-get-assertion-hid-cancel.html"; sourceTree = "<group>"; };
                5769C50A1D9B0001000847FB /* SerializedCryptoKeyWrap.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SerializedCryptoKeyWrap.mm; sourceTree = "<group>"; };
                5774AA6721FBBF7800AF2A1B /* TestSOAuthorization.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TestSOAuthorization.mm; sourceTree = "<group>"; };
                5778D05522110A2600899E3B /* LoadWebArchive.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = LoadWebArchive.mm; sourceTree = "<group>"; };
                                F4451C751EB8FD7C0020C5DA /* two-paragraph-contenteditable.html */,
                                CD57779B211CE6CE001B371E /* video-with-audio-and-web-audio.html */,
                                CD577798211CDE8F001B371E /* web-audio-only.html */,
+                               57663DF22357E45D00E85E09 /* web-authentication-get-assertion-hid-cancel.html */,
                                57663DEB234F1F8000E85E09 /* web-authentication-get-assertion-hid.html */,
                                57663DE9234EA60B00E85E09 /* web-authentication-get-assertion-nfc.html */,
                                57C6244F2346C1EC00383FE7 /* web-authentication-get-assertion.html */,
index 0c1a069..066fae7 100644 (file)
@@ -56,16 +56,15 @@ TEST(FidoHidMessageTest, TestPacketSize)
 }
 
 /*
- * U2f Init Packets are of the format:
- * Byte 0:    0
- * Byte 1-4:  Channel ID
- * Byte 5:    Command byte
- * Byte 6-7:  Big Endian size of data
- * Byte 8-n:  Data block
+ * CTAP HID Init Packets are of the format:
+ * Byte 0-3:  Channel ID
+ * Byte 4:    Command byte
+ * Byte 5-6:  Big Endian size of data
+ * Byte 7-n:  Data block
  *
  * Remaining buffer is padded with 0
  */
-TEST(FidoHidMessageTest, TestPacketData)
+TEST(FidoHidMessageTest, TestPacketData1)
 {
     uint32_t channelId = 0xF5060708;
     Vector<uint8_t> data {10, 11};
@@ -88,6 +87,34 @@ TEST(FidoHidMessageTest, TestPacketData)
         EXPECT_EQ(0, serialized[index]) << "mismatch at index " << index;
 }
 
+/*
+ * CTAP HID Continuation Packets are of the format:
+ * Byte 0-3:  Channel ID
+ * Byte 4:    SEQ
+ * Byte 5-n:  Data block
+ *
+ * Remaining buffer is padded with 0
+ */
+TEST(FidoHidMessageTest, TestPacketData2)
+{
+    uint32_t channelId = 0xF5060708;
+    Vector<uint8_t> data {10, 11};
+    auto initPacket = makeUnique<FidoHidContinuationPacket>(channelId, 0, Vector<uint8_t>(data));
+    size_t index = 0;
+
+    Vector<uint8_t> serialized = initPacket->getSerializedData();
+    EXPECT_EQ((channelId >> 24) & 0xff, serialized[index++]);
+    EXPECT_EQ((channelId >> 16) & 0xff, serialized[index++]);
+    EXPECT_EQ((channelId >> 8) & 0xff, serialized[index++]);
+    EXPECT_EQ(channelId & 0xff, serialized[index++]);
+    EXPECT_EQ(0, serialized[index++]);
+
+    EXPECT_EQ(data[0], serialized[index++]);
+    EXPECT_EQ(data[1], serialized[index++]);
+    for (; index < serialized.size(); index++)
+        EXPECT_EQ(0, serialized[index]) << "mismatch at index " << index;
+}
+
 TEST(FidoHidMessageTest, TestPacketConstructors)
 {
     uint32_t channelId = 0x05060708;
@@ -209,6 +236,16 @@ TEST(FidoHidMessageTest, TestDeserialize)
     }
 }
 
+TEST(FidoHidMessageTest, TestProperties)
+{
+    uint32_t channelId = 0x05060708;
+    Vector<uint8_t> data;
+
+    auto message = FidoHidMessage::create(channelId, FidoHidDeviceCommand::kCancel, data);
+    EXPECT_EQ(channelId, message->channelId());
+    EXPECT_EQ(FidoHidDeviceCommand::kCancel, message->cmd());
+}
+
 } // namespace TestWebKitAPI
 
 #endif // ENABLE(WEB_AUTHN)
index 184087c..e7bb606 100644 (file)
@@ -29,6 +29,7 @@
 #if ENABLE(WEB_AUTHN)
 
 #import "PlatformUtilities.h"
+#import "TCPServer.h"
 #import "TestWKWebView.h"
 #import "WKWebViewConfigurationExtras.h"
 #import <WebKit/WKPreferencesPrivate.h>
 #import <WebKit/_WKExperimentalFeature.h>
 #import <WebKit/_WKWebAuthenticationPanel.h>
 #import <wtf/BlockPtr.h>
+#import <wtf/text/StringConcatenateNumbers.h>
 
 static bool webAuthenticationPanelRan = false;
 static bool webAuthenticationPanelFailed = false;
 static bool webAuthenticationPanelSucceded = false;
+static RetainPtr<_WKWebAuthenticationPanel> gPanel;
 
 @interface TestWebAuthenticationPanelDelegate : NSObject <_WKWebAuthenticationPanelDelegate>
 @end
@@ -67,7 +70,6 @@ static bool webAuthenticationPanelSucceded = false;
 @end
 
 @implementation TestWebAuthenticationPanelUIDelegate {
-    RetainPtr<_WKWebAuthenticationPanel> _panel;
     RetainPtr<TestWebAuthenticationPanelDelegate> _delegate;
     BlockPtr<void(_WKWebAuthenticationPanelResult)> _callback;
 }
@@ -85,10 +87,10 @@ static bool webAuthenticationPanelSucceded = false;
 
     _delegate = adoptNS([[TestWebAuthenticationPanelDelegate alloc] init]);
     ASSERT_NE(panel, nil);
-    _panel = panel;
-    [_panel setDelegate:_delegate.get()];
+    gPanel = panel;
+    [gPanel setDelegate:_delegate.get()];
 
-    EXPECT_WK_STREQ([_panel relyingPartyID], "");
+    EXPECT_TRUE([[gPanel relyingPartyID] isEqual:@""] || [[gPanel relyingPartyID] isEqual:@"localhost"]);
 
     if (_isRacy) {
         if (!_callback) {
@@ -106,6 +108,24 @@ namespace TestWebKitAPI {
 
 namespace {
 
+const char parentFrame[] = "<html><iframe id='theFrame' src='iFrame.html'></iframe></html>";
+const char subFrame[] =
+"<html>"
+"<input type='text' id='input'>"
+"<script>"
+"    if (window.internals) {"
+"        internals.setMockWebAuthenticationConfiguration({ hid: { expectCancel: true } });"
+"        internals.withUserGesture(() => { input.focus(); });"
+"    }"
+"    const options = {"
+"        publicKey: {"
+"            challenge: new Uint8Array(16)"
+"        }"
+"    };"
+"    navigator.credentials.get(options);"
+"</script>"
+"</html>";
+
 static _WKExperimentalFeature *webAuthenticationExperimentalFeature()
 {
     static RetainPtr<_WKExperimentalFeature> theFeature;
@@ -122,21 +142,12 @@ static _WKExperimentalFeature *webAuthenticationExperimentalFeature()
     return theFeature.get();
 }
 
-// Only focused documents can trigger WebAuthn.
-static void focus(TestWKWebView *webView)
-{
-#if PLATFORM(MAC)
-    [[webView hostWindow] makeFirstResponder:webView];
-#elif PLATFORM(IOS)
-    [webView becomeFirstResponder];
-#endif
-}
-
 static void reset()
 {
     webAuthenticationPanelRan = false;
     webAuthenticationPanelFailed = false;
     webAuthenticationPanelSucceded = false;
+    gPanel = nullptr;
 }
 
 } // namesapce;
@@ -149,7 +160,6 @@ TEST(WebAuthenticationPanel, NoPanelTimeout)
     [[configuration preferences] _setEnabled:YES forExperimentalFeature:webAuthenticationExperimentalFeature()];
 
     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
-    focus(webView.get());
 
     [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
     [webView waitForMessage:@"Operation timed out."];
@@ -163,7 +173,6 @@ TEST(WebAuthenticationPanel, NoPanelHidSuccess)
     [[configuration preferences] _setEnabled:YES forExperimentalFeature:webAuthenticationExperimentalFeature()];
 
     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
-    focus(webView.get());
 
     [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
     [webView waitForMessage:@"Succeeded!"];
@@ -174,13 +183,12 @@ TEST(WebAuthenticationPanel, PanelTimeout)
     reset();
     RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"web-authentication-get-assertion" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
 
-    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
     [[configuration preferences] _setEnabled:YES forExperimentalFeature:webAuthenticationExperimentalFeature()];
 
-    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration.get()]);
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
     auto delegate = adoptNS([[TestWebAuthenticationPanelUIDelegate alloc] init]);
     [webView setUIDelegate:delegate.get()];
-    focus(webView.get());
 
     [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
     Util::run(&webAuthenticationPanelRan);
@@ -198,7 +206,6 @@ TEST(WebAuthenticationPanel, PanelHidSuccess)
     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
     auto delegate = adoptNS([[TestWebAuthenticationPanelUIDelegate alloc] init]);
     [webView setUIDelegate:delegate.get()];
-    focus(webView.get());
 
     [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
     Util::run(&webAuthenticationPanelRan);
@@ -222,7 +229,6 @@ TEST(WebAuthenticationPanel, PanelRacy1)
     auto delegate = adoptNS([[TestWebAuthenticationPanelUIDelegate alloc] init]);
     [delegate setIsRacy:true];
     [webView setUIDelegate:delegate.get()];
-    focus(webView.get());
 
     [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
     Util::run(&webAuthenticationPanelRan);
@@ -243,7 +249,6 @@ TEST(WebAuthenticationPanel, PanelRacy2)
     auto delegate = adoptNS([[TestWebAuthenticationPanelUIDelegate alloc] init]);
     [delegate setIsRacy:true];
     [webView setUIDelegate:delegate.get()];
-    focus(webView.get());
 
     [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
     Util::run(&webAuthenticationPanelRan);
@@ -266,7 +271,6 @@ TEST(WebAuthenticationPanel, PanelTwice)
     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
     auto delegate = adoptNS([[TestWebAuthenticationPanelUIDelegate alloc] init]);
     [webView setUIDelegate:delegate.get()];
-    focus(webView.get());
 
     [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
     Util::run(&webAuthenticationPanelRan);
@@ -278,6 +282,174 @@ TEST(WebAuthenticationPanel, PanelTwice)
     Util::run(&webAuthenticationPanelSucceded);
 }
 
+TEST(WebAuthenticationPanel, ReloadHidCancel)
+{
+    reset();
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"web-authentication-get-assertion-hid-cancel" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+    [[configuration preferences] _setEnabled:YES forExperimentalFeature:webAuthenticationExperimentalFeature()];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
+    auto delegate = adoptNS([[TestWebAuthenticationPanelUIDelegate alloc] init]);
+    [webView setUIDelegate:delegate.get()];
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&webAuthenticationPanelRan);
+    [webView reload];
+    Util::run(&webAuthenticationPanelFailed);
+}
+
+TEST(WebAuthenticationPanel, LocationChangeHidCancel)
+{
+    reset();
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"web-authentication-get-assertion-hid-cancel" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    RetainPtr<NSURL> otherURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+    [[configuration preferences] _setEnabled:YES forExperimentalFeature:webAuthenticationExperimentalFeature()];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
+    auto delegate = adoptNS([[TestWebAuthenticationPanelUIDelegate alloc] init]);
+    [webView setUIDelegate:delegate.get()];
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&webAuthenticationPanelRan);
+    [webView evaluateJavaScript: [NSString stringWithFormat:@"location = '%@'", [otherURL absoluteString]] completionHandler:nil];
+    Util::run(&webAuthenticationPanelFailed);
+}
+
+TEST(WebAuthenticationPanel, NewLoadHidCancel)
+{
+    reset();
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"web-authentication-get-assertion-hid-cancel" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    RetainPtr<NSURL> otherURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+    [[configuration preferences] _setEnabled:YES forExperimentalFeature:webAuthenticationExperimentalFeature()];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
+    auto delegate = adoptNS([[TestWebAuthenticationPanelUIDelegate alloc] init]);
+    [webView setUIDelegate:delegate.get()];
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&webAuthenticationPanelRan);
+    [webView loadRequest:[NSURLRequest requestWithURL:otherURL.get()]];
+    Util::run(&webAuthenticationPanelFailed);
+}
+
+TEST(WebAuthenticationPanel, CloseHidCancel)
+{
+    reset();
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"web-authentication-get-assertion-hid-cancel" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+    [[configuration preferences] _setEnabled:YES forExperimentalFeature:webAuthenticationExperimentalFeature()];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
+    auto delegate = adoptNS([[TestWebAuthenticationPanelUIDelegate alloc] init]);
+    [webView setUIDelegate:delegate.get()];
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&webAuthenticationPanelRan);
+    [webView _close];
+    Util::run(&webAuthenticationPanelFailed);
+}
+
+TEST(WebAuthenticationPanel, SubFrameChangeLocationHidCancel)
+{
+    TCPServer server([parentFrame = String(parentFrame), subFrame = String(subFrame)] (int socket) {
+        NSString *firstResponse = [NSString stringWithFormat:
+            @"HTTP/1.1 200 OK\r\n"
+            "Content-Length: %d\r\n\r\n"
+            "%@",
+            parentFrame.length(),
+            (id)parentFrame
+        ];
+        NSString *secondResponse = [NSString stringWithFormat:
+            @"HTTP/1.1 200 OK\r\n"
+            "Content-Length: %d\r\n\r\n"
+            "%@",
+            subFrame.length(),
+            (id)subFrame
+        ];
+
+        TCPServer::read(socket);
+        TCPServer::write(socket, firstResponse.UTF8String, firstResponse.length);
+        TCPServer::read(socket);
+        TCPServer::write(socket, secondResponse.UTF8String, secondResponse.length);
+    });
+
+    auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+    [[configuration preferences] _setEnabled:YES forExperimentalFeature:webAuthenticationExperimentalFeature()];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
+    auto delegate = adoptNS([[TestWebAuthenticationPanelUIDelegate alloc] init]);
+    [webView setUIDelegate:delegate.get()];
+
+    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:(id)makeString("http://localhost:", static_cast<unsigned>(server.port()))]]];
+    Util::run(&webAuthenticationPanelRan);
+    [webView evaluateJavaScript:@"theFrame.src = 'simple.html'" completionHandler:nil];
+    Util::run(&webAuthenticationPanelFailed);
+}
+
+TEST(WebAuthenticationPanel, SubFrameDestructionHidCancel)
+{
+    TCPServer server([parentFrame = String(parentFrame), subFrame = String(subFrame)] (int socket) {
+        NSString *firstResponse = [NSString stringWithFormat:
+            @"HTTP/1.1 200 OK\r\n"
+            "Content-Length: %d\r\n\r\n"
+            "%@",
+            parentFrame.length(),
+            (id)parentFrame
+        ];
+        NSString *secondResponse = [NSString stringWithFormat:
+            @"HTTP/1.1 200 OK\r\n"
+            "Content-Length: %d\r\n\r\n"
+            "%@",
+            subFrame.length(),
+            (id)subFrame
+        ];
+
+        TCPServer::read(socket);
+        TCPServer::write(socket, firstResponse.UTF8String, firstResponse.length);
+        TCPServer::read(socket);
+        TCPServer::write(socket, secondResponse.UTF8String, secondResponse.length);
+    });
+
+    auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+    [[configuration preferences] _setEnabled:YES forExperimentalFeature:webAuthenticationExperimentalFeature()];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
+    auto delegate = adoptNS([[TestWebAuthenticationPanelUIDelegate alloc] init]);
+    [webView setUIDelegate:delegate.get()];
+
+    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:(id)makeString("http://localhost:", static_cast<unsigned>(server.port()))]]];
+    Util::run(&webAuthenticationPanelRan);
+    [webView evaluateJavaScript:@"theFrame.parentNode.removeChild(theFrame)" completionHandler:nil];
+    Util::run(&webAuthenticationPanelFailed);
+}
+
+TEST(WebAuthenticationPanel, PanelHidCancel)
+{
+    reset();
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"web-authentication-get-assertion-hid-cancel" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+    [[configuration preferences] _setEnabled:YES forExperimentalFeature:webAuthenticationExperimentalFeature()];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
+    auto delegate = adoptNS([[TestWebAuthenticationPanelUIDelegate alloc] init]);
+    [webView setUIDelegate:delegate.get()];
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&webAuthenticationPanelRan);
+    [gPanel cancel];
+    [webView waitForMessage:@"Operation timed out."];
+    EXPECT_FALSE(webAuthenticationPanelFailed);
+    EXPECT_FALSE(webAuthenticationPanelSucceded);
+}
+
 } // namespace TestWebKitAPI
 
 #endif // ENABLE(WEB_AUTHN)
diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-hid-cancel.html b/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-hid-cancel.html
new file mode 100644 (file)
index 0000000..cf12481
--- /dev/null
@@ -0,0 +1,22 @@
+<input type="text" id="input">
+<script>
+    if (window.internals) {
+        internals.setMockWebAuthenticationConfiguration({ hid: { expectCancel: true } });
+        internals.withUserGesture(() => { input.focus(); });
+    }
+
+    const options = {
+        publicKey: {
+            challenge: new Uint8Array(16),
+            timeout: 100
+        }
+    };
+
+    navigator.credentials.get(options).then(credential => {
+        // console.log("Succeeded!");
+        window.webkit.messageHandlers.testHandler.postMessage("Succeeded!");
+    }, error => {
+        // console.log(error.message);
+        window.webkit.messageHandlers.testHandler.postMessage(error.message);
+    });
+</script>
index b1ef5a3..65510d1 100644 (file)
@@ -1,3 +1,4 @@
+<input type="text" id="input">
 <script>
     const testAssertionMessageBase64 =
         "AKMBomJpZFhAKAitzuj+Tslzelf3/vZwIGtDQNgoKeFd5oEieYzhyzA65saf0tK2" +
@@ -5,8 +6,10 @@
         "Z51VstuQkuHI2eXh0Ct1gPC0gSx3CWLh5I9a2AEAAABQA1hHMEUCIQCSFTuuBWgB" +
         "4/F0VB7DlUVM09IHPmxe1MzHUwRoCRZbCAIgGKov6xoAx2MEf6/6qNs8OutzhP2C" +
         "QoJ1L7Fe64G9uBc=";
-    if (window.internals)
-        internals.setMockWebAuthenticationConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", payloadBase64: [testAssertionMessageBase64] } });
+    if (window.internals) {
+        internals.setMockWebAuthenticationConfiguration({ hid: { payloadBase64: [testAssertionMessageBase64] } });
+        internals.withUserGesture(() => { input.focus(); });
+    }
 
     const options = {
         publicKey: {
index 706a6b7..945b6b6 100644 (file)
@@ -1,3 +1,4 @@
+<input type="text" id="input">
 <script>
     const testNfcCtapVersionBase64 = "RklET18yXzCQAA==";
     const testGetInfoResponseApduBase64 =
@@ -9,8 +10,10 @@
         "Z51VstuQkuHI2eXh0Ct1gPC0gSx3CWLh5I9a2AEAAABQA1hHMEUCIQCSFTuuBWgB" +
         "4/F0VB7DlUVM09IHPmxe1MzHUwRoCRZbCAIgGKov6xoAx2MEf6/6qNs8OutzhP2C" +
         "QoJ1L7Fe64G9uBeQAA==";
-    if (window.internals)
-        internals.setMockWebAuthenticationConfiguration({ nfc: { error: "success", payloadBase64: [testNfcCtapVersionBase64, testGetInfoResponseApduBase64, testAssertionMessageApduBase64] } });
+    if (window.internals) {
+        internals.setMockWebAuthenticationConfiguration({ nfc: { payloadBase64: [testNfcCtapVersionBase64, testGetInfoResponseApduBase64, testAssertionMessageApduBase64] } });
+        internals.withUserGesture(() => { input.focus(); });
+    }
 
     const options = {
         publicKey: {
index bf49a5f..2de2e92 100644 (file)
@@ -1,4 +1,8 @@
+<input type="text" id="input">
 <script>
+    if (window.internals)
+        internals.withUserGesture(() => { input.focus(); });
+
     const options = {
         publicKey: {
             challenge: new Uint8Array(16),