WebKit Bugzilla
Attachment 358561 Details for
Bug 191535
: [WebAuthN] Support U2F HID Authenticators on macOS
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-191535-20190107180213.patch (text/plain), 142.89 KB, created by
Jiewen Tan
on 2019-01-07 18:02:14 PST
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Jiewen Tan
Created:
2019-01-07 18:02:14 PST
Size:
142.89 KB
patch
obsolete
>Subversion Revision: 239718 >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 03bc51d848a66e4b5d56a76064074105bc23b0ef..3c4853de16f8befcb777604c26acad8158566aca 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,30 @@ >+2019-01-07 Jiewen Tan <jiewen_tan@apple.com> >+ >+ [WebAuthN] Support U2F HID Authenticators on macOS >+ https://bugs.webkit.org/show_bug.cgi?id=191535 >+ <rdar://problem/47102027> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ This patch changes U2fCommandConstructor to produce register commands with >+ enforcing test of user presence. Otherwise, authenticators would silently >+ generate credentials. It also renames readFromU2fSignResponse to >+ readU2fSignResponse. >+ >+ Tests: http/wpt/webauthn/public-key-credential-create-failure-u2f-silent.https.html >+ http/wpt/webauthn/public-key-credential-create-failure-u2f.https.html >+ http/wpt/webauthn/public-key-credential-create-success-u2f.https.html >+ http/wpt/webauthn/public-key-credential-get-failure-u2f-silent.https.html >+ http/wpt/webauthn/public-key-credential-get-failure-u2f.https.html >+ http/wpt/webauthn/public-key-credential-get-success-u2f.https.html >+ >+ * Modules/webauthn/fido/U2fCommandConstructor.cpp: >+ (fido::WebCore::constructU2fRegisterCommand): >+ * Modules/webauthn/fido/U2fResponseConverter.cpp: >+ (fido::readU2fSignResponse): >+ (fido::readFromU2fSignResponse): Deleted. >+ * Modules/webauthn/fido/U2fResponseConverter.h: >+ > 2019-01-07 Youenn Fablet <youenn@apple.com> > > Crash in SWServer::Connection::resolveRegistrationReadyRequests >diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog >index 47fb014222a2a55ff7091d9cbfed6cb60406bc83..c205fce7a801a6b86fa30d64ac931ca88d30c781 100644 >--- a/Source/WebKit/ChangeLog >+++ b/Source/WebKit/ChangeLog >@@ -1,3 +1,71 @@ >+2019-01-07 Jiewen Tan <jiewen_tan@apple.com> >+ >+ [WebAuthN] Support U2F HID Authenticators on macOS >+ https://bugs.webkit.org/show_bug.cgi?id=191535 >+ <rdar://problem/47102027> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ This patch implements the support for U2F authenticators, and enables it for hid devices. >+ It follows the CTAP spec to map WebAuthN requests to U2F commands and return the responses: >+ https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#u2f-interoperability >+ Most of the parts are done before this patch, this patch focues on: 7.2.2 and 7.3.2. >+ >+ Besides implementing the U2fHidAuthenticator, this patch also: >+ 1. renames UIProcess/WebAuthentication/fido to UIProcess/WebAuthentication/Fido to be consistent >+ with others; >+ 2. adds support in the mocking environment for U2F authenticators. It is done by extending >+ the stages in MockHidConnection from 4 to indefinite as multi-round communications are expected >+ to map WebAuthN requests to U2F requests. >+ >+ * Sources.txt: >+ * UIProcess/API/C/WKWebsiteDataStoreRef.cpp: >+ (WKWebsiteDataStoreSetWebAuthenticationMockConfiguration): >+ * UIProcess/WebAuthentication/Cocoa/HidService.mm: >+ (WebKit::HidService::continueAddDeviceAfterGetInfo): >+ * UIProcess/WebAuthentication/Fido/CtapHidAuthenticator.cpp: Renamed from Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidAuthenticator.cpp. >+ (WebKit::CtapHidAuthenticator::CtapHidAuthenticator): >+ (WebKit::CtapHidAuthenticator::makeCredential): >+ (WebKit::CtapHidAuthenticator::continueMakeCredentialAfterResponseReceived const): >+ (WebKit::CtapHidAuthenticator::getAssertion): >+ (WebKit::CtapHidAuthenticator::continueGetAssertionAfterResponseReceived const): >+ * UIProcess/WebAuthentication/Fido/CtapHidAuthenticator.h: Copied from Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidAuthenticator.h. >+ * UIProcess/WebAuthentication/Fido/CtapHidDriver.cpp: Renamed from Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidDriver.cpp. >+ (WebKit::CtapHidDriver::Worker::Worker): >+ (WebKit::CtapHidDriver::Worker::~Worker): >+ (WebKit::CtapHidDriver::Worker::transact): >+ (WebKit::CtapHidDriver::Worker::write): >+ (WebKit::CtapHidDriver::Worker::read): >+ (WebKit::CtapHidDriver::Worker::returnMessage): >+ (WebKit::CtapHidDriver::CtapHidDriver): >+ (WebKit::CtapHidDriver::transact): >+ (WebKit::CtapHidDriver::continueAfterChannelAllocated): >+ (WebKit::CtapHidDriver::continueAfterResponseReceived): >+ (WebKit::CtapHidDriver::returnResponse): >+ * UIProcess/WebAuthentication/Fido/CtapHidDriver.h: Renamed from Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidDriver.h. >+ (WebKit::CtapHidDriver::setProtocol): >+ * UIProcess/WebAuthentication/Fido/U2fHidAuthenticator.cpp: Added. >+ (WebKit::U2fHidAuthenticator::U2fHidAuthenticator): >+ (WebKit::U2fHidAuthenticator::makeCredential): >+ (WebKit::U2fHidAuthenticator::checkExcludeList): >+ (WebKit::U2fHidAuthenticator::issueRegisterCommand): >+ (WebKit::U2fHidAuthenticator::getAssertion): >+ (WebKit::U2fHidAuthenticator::issueSignCommand): >+ (WebKit::U2fHidAuthenticator::issueNewCommand): >+ (WebKit::U2fHidAuthenticator::issueCommand): >+ (WebKit::U2fHidAuthenticator::responseReceived): >+ (WebKit::U2fHidAuthenticator::continueRegisterCommandAfterResponseReceived): >+ (WebKit::U2fHidAuthenticator::continueCheckOnlyCommandAfterResponseReceived): >+ (WebKit::U2fHidAuthenticator::continueBogusCommandAfterResponseReceived): >+ (WebKit::U2fHidAuthenticator::continueSignCommandAfterResponseReceived): >+ * UIProcess/WebAuthentication/Fido/U2fHidAuthenticator.h: Renamed from Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidAuthenticator.h. >+ * UIProcess/WebAuthentication/Mock/MockHidConnection.cpp: >+ (WebKit::MockHidConnection::parseRequest): >+ (WebKit::MockHidConnection::feedReports): >+ * UIProcess/WebAuthentication/Mock/MockHidConnection.h: >+ * UIProcess/WebAuthentication/Mock/MockWebAuthenticationConfiguration.h: >+ * WebKit.xcodeproj/project.pbxproj: >+ > 2019-01-07 Alex Christensen <achristensen@webkit.org> > > Remove use of NetworkProcess::singleton from CacheStorage::Engine::from >diff --git a/Source/WebCore/Modules/webauthn/fido/U2fCommandConstructor.cpp b/Source/WebCore/Modules/webauthn/fido/U2fCommandConstructor.cpp >index 1606ab0d13488a8a08e5eb304adb630d3435e0c9..21ba609243fb0d1e3596e39c9367113a6d5b8dca 100644 >--- a/Source/WebCore/Modules/webauthn/fido/U2fCommandConstructor.cpp >+++ b/Source/WebCore/Modules/webauthn/fido/U2fCommandConstructor.cpp >@@ -54,6 +54,8 @@ static Vector<uint8_t> constructU2fRegisterCommand(const Vector<uint8_t>& applic > > apdu::ApduCommand command; > command.setIns(static_cast<uint8_t>(U2fApduInstruction::kRegister)); >+ // This is needed for test of user presence even though the spec doesn't specify it. >+ command.setP1(kP1EnforceUserPresenceAndSign); > command.setData(WTFMove(data)); > command.setResponseLength(apdu::ApduCommand::kApduMaxResponseLength); > return command.getEncodedCommand(); >diff --git a/Source/WebCore/Modules/webauthn/fido/U2fResponseConverter.cpp b/Source/WebCore/Modules/webauthn/fido/U2fResponseConverter.cpp >index c45a1711799021356ba785cef16c28a91a301be0..cb4678168b4b82d1e4ae7e0a77cd1a254a873624 100644 >--- a/Source/WebCore/Modules/webauthn/fido/U2fResponseConverter.cpp >+++ b/Source/WebCore/Modules/webauthn/fido/U2fResponseConverter.cpp >@@ -173,7 +173,7 @@ Optional<PublicKeyCredentialData> readU2fRegisterResponse(const String& rpId, co > return PublicKeyCredentialData { ArrayBuffer::create(credentialId.data(), credentialId.size()), true, nullptr, ArrayBuffer::create(attestationObject.data(), attestationObject.size()), nullptr, nullptr, nullptr }; > } > >-Optional<PublicKeyCredentialData> readFromU2fSignResponse(const String& rpId, const Vector<uint8_t>& keyHandle, const Vector<uint8_t>& u2fData) >+Optional<PublicKeyCredentialData> readU2fSignResponse(const String& rpId, const Vector<uint8_t>& keyHandle, const Vector<uint8_t>& u2fData) > { > if (keyHandle.isEmpty() || u2fData.size() <= signatureIndex) > return WTF::nullopt; >diff --git a/Source/WebCore/Modules/webauthn/fido/U2fResponseConverter.h b/Source/WebCore/Modules/webauthn/fido/U2fResponseConverter.h >index d6a41f873d8bbc4f9a5a0f07e30d67f27df2abcc..4c6d24e5b4f7e22a66b334f5f8734f9e4f148c42 100644 >--- a/Source/WebCore/Modules/webauthn/fido/U2fResponseConverter.h >+++ b/Source/WebCore/Modules/webauthn/fido/U2fResponseConverter.h >@@ -42,7 +42,7 @@ WEBCORE_EXPORT Optional<WebCore::PublicKeyCredentialData> readU2fRegisterRespons > > // Converts a U2F authentication response to WebAuthN getAssertion response. > // https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#u2f-authenticatorGetAssertion-interoperability >-WEBCORE_EXPORT Optional<WebCore::PublicKeyCredentialData> readFromU2fSignResponse(const String& rpId, const Vector<uint8_t>& keyHandle, const Vector<uint8_t>& u2fData); >+WEBCORE_EXPORT Optional<WebCore::PublicKeyCredentialData> readU2fSignResponse(const String& rpId, const Vector<uint8_t>& keyHandle, const Vector<uint8_t>& u2fData); > > } // namespace fido > >diff --git a/Source/WebKit/Sources.txt b/Source/WebKit/Sources.txt >index e0a3282e0092d97ce35d6646b4331b8ca11f48e0..48b1d310f875f4399a4637ea594eaeda0e787656 100644 >--- a/Source/WebKit/Sources.txt >+++ b/Source/WebKit/Sources.txt >@@ -384,6 +384,10 @@ UIProcess/Plugins/PluginProcessProxy.cpp > UIProcess/UserContent/WebScriptMessageHandler.cpp > UIProcess/UserContent/WebUserContentControllerProxy.cpp > >+UIProcess/WebAuthentication/Fido/CtapHidAuthenticator.cpp >+UIProcess/WebAuthentication/Fido/CtapHidDriver.cpp >+UIProcess/WebAuthentication/Fido/U2fHidAuthenticator.cpp >+ > UIProcess/WebAuthentication/Mock/MockAuthenticatorManager.cpp > UIProcess/WebAuthentication/Mock/MockHidConnection.cpp > UIProcess/WebAuthentication/Mock/MockHidService.cpp >diff --git a/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.cpp b/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.cpp >index a3e3468256bb67008295fc13f072df379b70012c..fb54933133f1aa7c1d731fb61f4d798a81480404 100644 >--- a/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.cpp >+++ b/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.cpp >@@ -627,8 +627,11 @@ void WKWebsiteDataStoreSetWebAuthenticationMockConfiguration(WKWebsiteDataStoreR > if (error == "wrong-nonce") > hid.error = MockWebAuthenticationConfiguration::Hid::Error::WrongNonce; > >- if (auto payloadBase64 = static_cast<WKStringRef>(WKDictionaryGetItemForKey(hidRef, adoptWK(WKStringCreateWithUTF8CString("PayloadBase64")).get()))) >- hid.payloadBase64 = WebKit::toImpl(payloadBase64)->string(); >+ if (auto payloadBase64 = static_cast<WKArrayRef>(WKDictionaryGetItemForKey(hidRef, adoptWK(WKStringCreateWithUTF8CString("PayloadBase64")).get()))) >+ hid.payloadBase64 = WebKit::toImpl(payloadBase64)->toStringVector(); >+ >+ if (auto isU2f = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(hidRef, adoptWK(WKStringCreateWithUTF8CString("IsU2f")).get()))) >+ hid.isU2f = WKBooleanGetValue(isU2f); > > if (auto keepAlive = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(hidRef, adoptWK(WKStringCreateWithUTF8CString("KeepAlive")).get()))) > hid.keepAlive = WKBooleanGetValue(keepAlive); >diff --git a/Source/WebKit/UIProcess/WebAuthentication/Cocoa/HidService.mm b/Source/WebKit/UIProcess/WebAuthentication/Cocoa/HidService.mm >index 237b1b460728119132df79839ca6764535d63eef..d8e4e41ac26f87b550ed181f46031d20fd24074e 100644 >--- a/Source/WebKit/UIProcess/WebAuthentication/Cocoa/HidService.mm >+++ b/Source/WebKit/UIProcess/WebAuthentication/Cocoa/HidService.mm >@@ -31,6 +31,7 @@ > #import "CtapHidAuthenticator.h" > #import "CtapHidDriver.h" > #import "HidConnection.h" >+#import "U2fHidAuthenticator.h" > #import <WebCore/DeviceRequestConverter.h> > #import <WebCore/DeviceResponseConverter.h> > #import <WebCore/FidoConstants.h> >@@ -108,7 +109,7 @@ void HidService::deviceAdded(IOHIDDeviceRef device) > void HidService::continueAddDeviceAfterGetInfo(CtapHidDriver* ptr, Vector<uint8_t>&& response) > { > std::unique_ptr<CtapHidDriver> driver = m_drivers.take(ptr); >- if (!driver || !observer()) >+ if (!driver || !observer() || response.isEmpty()) > return; > > auto info = readCTAPGetInfoResponse(response); >@@ -118,6 +119,8 @@ void HidService::continueAddDeviceAfterGetInfo(CtapHidDriver* ptr, Vector<uint8_ > } > // FIXME(191535): Support U2F authenticators. > LOG_ERROR("Couldn't parse a ctap get info response."); >+ driver->setProtocol(ProtocolVersion::kU2f); >+ observer()->authenticatorAdded(U2fHidAuthenticator::create(WTFMove(driver))); > } > > } // namespace WebKit >diff --git a/Source/WebKit/UIProcess/WebAuthentication/Fido/CtapHidAuthenticator.cpp b/Source/WebKit/UIProcess/WebAuthentication/Fido/CtapHidAuthenticator.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..ab42af66f91079a197d5c3d3bc872be2a3577126 >--- /dev/null >+++ b/Source/WebKit/UIProcess/WebAuthentication/Fido/CtapHidAuthenticator.cpp >@@ -0,0 +1,98 @@ >+/* >+ * 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. >+ */ >+ >+#include "config.h" >+#include "CtapHidAuthenticator.h" >+ >+#if ENABLE(WEB_AUTHN) && PLATFORM(MAC) >+ >+#include "CtapHidDriver.h" >+#include <WebCore/DeviceRequestConverter.h> >+#include <WebCore/DeviceResponseConverter.h> >+#include <WebCore/ExceptionData.h> >+#include <wtf/RunLoop.h> >+#include <wtf/text/StringConcatenateNumbers.h> >+ >+namespace WebKit { >+using namespace WebCore; >+using namespace fido; >+ >+CtapHidAuthenticator::CtapHidAuthenticator(std::unique_ptr<CtapHidDriver>&& driver, AuthenticatorGetInfoResponse&& info) >+ : m_driver(WTFMove(driver)) >+ , m_info(WTFMove(info)) >+{ >+ // FIXME(191520): We need a way to convert std::unique_ptr to UniqueRef. >+ ASSERT(m_driver); >+} >+ >+void CtapHidAuthenticator::makeCredential() >+{ >+ // FIXME(192061) >+ LOG_ERROR("Start making credentials."); >+ auto cborCmd = encodeMakeCredenitalRequestAsCBOR(requestData().hash, requestData().creationOptions, m_info.options().userVerificationAvailability()); >+ m_driver->transact(WTFMove(cborCmd), [weakThis = makeWeakPtr(*this)](Vector<uint8_t>&& data) { >+ ASSERT(RunLoop::isMain()); >+ if (!weakThis) >+ return; >+ weakThis->continueMakeCredentialAfterResponseReceived(WTFMove(data)); >+ }); >+} >+ >+void CtapHidAuthenticator::continueMakeCredentialAfterResponseReceived(Vector<uint8_t>&& data) const >+{ >+ auto response = readCTAPMakeCredentialResponse(data); >+ if (!response) { >+ receiveRespond(ExceptionData { UnknownError, makeString("Unknown internal error. Error code: ", data.size() == 1 ? data[0] : -1) }); >+ return; >+ } >+ receiveRespond(WTFMove(*response)); >+} >+ >+void CtapHidAuthenticator::getAssertion() >+{ >+ // FIXME(192061) >+ LOG_ERROR("Start getting assertions."); >+ auto cborCmd = encodeGetAssertionRequestAsCBOR(requestData().hash, requestData().requestOptions, m_info.options().userVerificationAvailability()); >+ m_driver->transact(WTFMove(cborCmd), [weakThis = makeWeakPtr(*this)](Vector<uint8_t>&& data) { >+ ASSERT(RunLoop::isMain()); >+ if (!weakThis) >+ return; >+ weakThis->continueGetAssertionAfterResponseReceived(WTFMove(data)); >+ }); >+} >+ >+void CtapHidAuthenticator::continueGetAssertionAfterResponseReceived(Vector<uint8_t>&& data) const >+{ >+ auto response = readCTAPGetAssertionResponse(data); >+ if (!response) { >+ receiveRespond(ExceptionData { UnknownError, makeString("Unknown internal error. Error code: ", data.size() == 1 ? data[0] : -1) }); >+ return; >+ } >+ receiveRespond(WTFMove(*response)); >+} >+ >+} // namespace WebKit >+ >+#endif // ENABLE(WEB_AUTHN) && PLATFORM(MAC) >diff --git a/Source/WebKit/UIProcess/WebAuthentication/Fido/CtapHidAuthenticator.h b/Source/WebKit/UIProcess/WebAuthentication/Fido/CtapHidAuthenticator.h >new file mode 100644 >index 0000000000000000000000000000000000000000..fdf6279525691dec5fa8d356e0ae19f2bf207b66 >--- /dev/null >+++ b/Source/WebKit/UIProcess/WebAuthentication/Fido/CtapHidAuthenticator.h >@@ -0,0 +1,58 @@ >+/* >+ * 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. >+ */ >+ >+#pragma once >+ >+#if ENABLE(WEB_AUTHN) && PLATFORM(MAC) >+ >+#include "Authenticator.h" >+#include <WebCore/AuthenticatorGetInfoResponse.h> >+ >+namespace WebKit { >+ >+class CtapHidDriver; >+ >+class CtapHidAuthenticator final : public Authenticator { >+public: >+ static Ref<CtapHidAuthenticator> create(std::unique_ptr<CtapHidDriver>&& driver, fido::AuthenticatorGetInfoResponse&& info) >+ { >+ return adoptRef(*new CtapHidAuthenticator(WTFMove(driver), WTFMove(info))); >+ } >+ >+private: >+ explicit CtapHidAuthenticator(std::unique_ptr<CtapHidDriver>&&, fido::AuthenticatorGetInfoResponse&&); >+ >+ void makeCredential() final; >+ void continueMakeCredentialAfterResponseReceived(Vector<uint8_t>&&) const; >+ void getAssertion() final; >+ void continueGetAssertionAfterResponseReceived(Vector<uint8_t>&&) const; >+ >+ std::unique_ptr<CtapHidDriver> m_driver; >+ fido::AuthenticatorGetInfoResponse m_info; >+}; >+ >+} // namespace WebKit >+ >+#endif // ENABLE(WEB_AUTHN) && PLATFORM(MAC) >diff --git a/Source/WebKit/UIProcess/WebAuthentication/Fido/CtapHidDriver.cpp b/Source/WebKit/UIProcess/WebAuthentication/Fido/CtapHidDriver.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..1f9ef98394fee47953f9c40b8a25b9b4b77fde9b >--- /dev/null >+++ b/Source/WebKit/UIProcess/WebAuthentication/Fido/CtapHidDriver.cpp >@@ -0,0 +1,228 @@ >+/* >+ * 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. >+ */ >+ >+#include "config.h" >+#include "CtapHidDriver.h" >+ >+#if ENABLE(WEB_AUTHN) && PLATFORM(MAC) >+ >+#include <WebCore/FidoConstants.h> >+#include <wtf/CryptographicallyRandomNumber.h> >+#include <wtf/RunLoop.h> >+#include <wtf/Vector.h> >+#include <wtf/text/Base64.h> >+ >+namespace WebKit { >+using namespace fido; >+ >+CtapHidDriver::Worker::Worker(UniqueRef<HidConnection>&& connection) >+ : m_connection(WTFMove(connection)) >+{ >+ m_connection->initialize(); >+} >+ >+CtapHidDriver::Worker::~Worker() >+{ >+ m_connection->terminate(); >+} >+ >+void CtapHidDriver::Worker::transact(fido::FidoHidMessage&& requestMessage, MessageCallback&& callback) >+{ >+ ASSERT(m_state == State::Idle); >+ m_state = State::Write; >+ m_requestMessage = WTFMove(requestMessage); >+ m_responseMessage.reset(); >+ m_callback = WTFMove(callback); >+ >+ // HidConnection could hold data from other applications, and thereofore invalidate it before each transaction. >+ m_connection->invalidateCache(); >+ m_connection->send(m_requestMessage->popNextPacket(), [weakThis = makeWeakPtr(*this)](HidConnection::DataSent sent) mutable { >+ ASSERT(RunLoop::isMain()); >+ if (!weakThis) >+ return; >+ weakThis->write(sent); >+ }); >+} >+ >+void CtapHidDriver::Worker::write(HidConnection::DataSent sent) >+{ >+ ASSERT(m_state == State::Write); >+ // FIXME(192061) >+ LOG_ERROR("Start writing data."); >+ if (sent != HidConnection::DataSent::Yes) { >+ returnMessage(WTF::nullopt); >+ return; >+ } >+ >+ if (!m_requestMessage->numPackets()) { >+ m_state = State::Read; >+ m_connection->registerDataReceivedCallback([weakThis = makeWeakPtr(*this)](Vector<uint8_t>&& data) mutable { >+ ASSERT(RunLoop::isMain()); >+ if (!weakThis) >+ return; >+ weakThis->read(data); >+ }); >+ return; >+ } >+ >+ m_connection->send(m_requestMessage->popNextPacket(), [weakThis = makeWeakPtr(*this)](HidConnection::DataSent sent) mutable { >+ ASSERT(RunLoop::isMain()); >+ if (!weakThis) >+ return; >+ weakThis->write(sent); >+ }); >+} >+ >+void CtapHidDriver::Worker::read(const Vector<uint8_t>& data) >+{ >+ ASSERT(m_state == State::Read); >+ // FIXME(192061) >+ LOG_ERROR("Start reading data."); >+ if (!m_responseMessage) { >+ m_responseMessage = FidoHidMessage::createFromSerializedData(data); >+ // The first few reports could be for other applications, and therefore ignore those. >+ if (!m_responseMessage || m_responseMessage->channelId() != m_requestMessage->channelId()) { >+ LOG_ERROR("Couldn't parse a hid init packet: %s", m_responseMessage ? "wrong channel id." : "bad data."); >+ m_responseMessage.reset(); >+ return; >+ } >+ } else { >+ if (!m_responseMessage->addContinuationPacket(data)) { >+ LOG_ERROR("Couldn't parse a hid continuation packet."); >+ returnMessage(WTF::nullopt); >+ return; >+ } >+ } >+ >+ if (m_responseMessage->messageComplete()) { >+ // A KeepAlive cmd could be sent between a request and a response to indicate that >+ // the authenticator is waiting for user consent. Keep listening for the response. >+ if (m_responseMessage->cmd() == FidoHidDeviceCommand::kKeepAlive) { >+ m_responseMessage.reset(); >+ return; >+ } >+ returnMessage(WTFMove(m_responseMessage)); >+ return; >+ } >+} >+ >+void CtapHidDriver::Worker::returnMessage(Optional<fido::FidoHidMessage>&& message) >+{ >+ // FIXME(192061) >+ LOG_ERROR("Start returning data."); >+ m_state = State::Idle; >+ m_connection->unregisterDataReceivedCallback(); >+ m_callback(WTFMove(message)); >+} >+ >+CtapHidDriver::CtapHidDriver(UniqueRef<HidConnection>&& connection) >+ : m_worker(makeUniqueRef<Worker>(WTFMove(connection))) >+ , m_nonce(kHidInitNonceLength) >+{ >+} >+ >+void CtapHidDriver::transact(Vector<uint8_t>&& data, ResponseCallback&& callback) >+{ >+ ASSERT(m_state == State::Idle); >+ m_state = State::AllocateChannel; >+ m_channelId = kHidBroadcastChannel; >+ m_requestData = WTFMove(data); >+ m_responseCallback = WTFMove(callback); >+ >+ // Allocate a channel. >+ // FIXME(192061) >+ LOG_ERROR("Start allocating a channel."); >+ ASSERT(m_nonce.size() == kHidInitNonceLength); >+ cryptographicallyRandomValues(m_nonce.data(), m_nonce.size()); >+ auto initCommand = FidoHidMessage::create(m_channelId, FidoHidDeviceCommand::kInit, m_nonce); >+ ASSERT(initCommand); >+ m_worker->transact(WTFMove(*initCommand), [weakThis = makeWeakPtr(*this)](Optional<FidoHidMessage>&& response) mutable { >+ ASSERT(RunLoop::isMain()); >+ if (!weakThis) >+ return; >+ weakThis->continueAfterChannelAllocated(WTFMove(response)); >+ }); >+} >+ >+void CtapHidDriver::continueAfterChannelAllocated(Optional<FidoHidMessage>&& message) >+{ >+ ASSERT(m_state == State::AllocateChannel); >+ if (!message) { >+ returnResponse({ }); >+ return; >+ } >+ ASSERT(message->channelId() == m_channelId); >+ >+ auto payload = message->getMessagePayload(); >+ ASSERT(payload.size() == kHidInitResponseSize); >+ // Restart the transaction in the next run loop when nonce mismatches. >+ if (memcmp(payload.data(), m_nonce.data(), m_nonce.size())) { >+ m_state = State::Idle; >+ RunLoop::main().dispatch([weakThis = makeWeakPtr(*this), data = WTFMove(m_requestData), callback = WTFMove(m_responseCallback)]() mutable { >+ if (!weakThis) >+ return; >+ weakThis->transact(WTFMove(data), WTFMove(callback)); >+ }); >+ return; >+ } >+ >+ m_state = State::Ready; >+ auto index = kHidInitNonceLength; >+ m_channelId = static_cast<uint32_t>(payload[index++]) << 24; >+ 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(192061) >+ LOG_ERROR("Start sending the request."); >+ auto cmd = FidoHidMessage::create(m_channelId, m_protocol == ProtocolVersion::kCtap ? FidoHidDeviceCommand::kCbor : FidoHidDeviceCommand::kMsg, m_requestData); >+ ASSERT(cmd); >+ m_worker->transact(WTFMove(*cmd), [weakThis = makeWeakPtr(*this)](Optional<FidoHidMessage>&& response) mutable { >+ ASSERT(RunLoop::isMain()); >+ if (!weakThis) >+ return; >+ weakThis->continueAfterResponseReceived(WTFMove(response)); >+ }); >+} >+ >+void CtapHidDriver::continueAfterResponseReceived(Optional<fido::FidoHidMessage>&& message) >+{ >+ ASSERT(m_state == State::Ready); >+ ASSERT(!message || message->channelId() == m_channelId); >+ // FIXME(192061) >+ LOG_ERROR("Start returning the response."); >+ returnResponse(message ? message->getMessagePayload() : Vector<uint8_t>()); >+} >+ >+void CtapHidDriver::returnResponse(Vector<uint8_t>&& response) >+{ >+ // Reset state before calling the response callback to avoid being deleted. >+ m_state = State::Idle; >+ m_responseCallback(WTFMove(response)); >+} >+ >+} // namespace WebKit >+ >+#endif // ENABLE(WEB_AUTHN) && PLATFORM(MAC) >diff --git a/Source/WebKit/UIProcess/WebAuthentication/Fido/CtapHidDriver.h b/Source/WebKit/UIProcess/WebAuthentication/Fido/CtapHidDriver.h >new file mode 100644 >index 0000000000000000000000000000000000000000..8fb504d91e978370b0f6d942818cd198797823cc >--- /dev/null >+++ b/Source/WebKit/UIProcess/WebAuthentication/Fido/CtapHidDriver.h >@@ -0,0 +1,112 @@ >+/* >+ * 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. >+ */ >+ >+#pragma once >+ >+#if ENABLE(WEB_AUTHN) && PLATFORM(MAC) >+ >+#include "HidConnection.h" >+#include <WebCore/FidoConstants.h> >+#include <WebCore/FidoHidMessage.h> >+#include <wtf/CompletionHandler.h> >+#include <wtf/Forward.h> >+#include <wtf/Noncopyable.h> >+#include <wtf/UniqueRef.h> >+#include <wtf/WeakPtr.h> >+ >+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 CanMakeWeakPtr<CtapHidDriver> { >+ WTF_MAKE_FAST_ALLOCATED; >+ WTF_MAKE_NONCOPYABLE(CtapHidDriver); >+public: >+ using ResponseCallback = Function<void(Vector<uint8_t>&&)>; >+ >+ enum class State : uint8_t { >+ Idle, >+ AllocateChannel, >+ Ready, >+ // FIXME(191528) >+ Busy >+ }; >+ >+ explicit CtapHidDriver(UniqueRef<HidConnection>&&); >+ >+ void setProtocol(fido::ProtocolVersion protocol) { m_protocol = protocol; } >+ void transact(Vector<uint8_t>&& data, ResponseCallback&&); >+ >+private: >+ // Worker is the helper that maintains the transaction. >+ // https://fidoalliance.org/specs/fido-v2.0-ps-20170927/fido-client-to-authenticator-protocol-v2.0-ps-20170927.html#arbitration >+ // FSM: Idle => Write => Read. >+ class Worker : public CanMakeWeakPtr<Worker> { >+ WTF_MAKE_FAST_ALLOCATED; >+ WTF_MAKE_NONCOPYABLE(Worker); >+ public: >+ using MessageCallback = Function<void(Optional<fido::FidoHidMessage>&&)>; >+ >+ enum class State : uint8_t { >+ Idle, >+ Write, >+ Read >+ }; >+ >+ explicit Worker(UniqueRef<HidConnection>&&); >+ ~Worker(); >+ >+ void transact(fido::FidoHidMessage&&, MessageCallback&&); >+ >+ private: >+ void write(HidConnection::DataSent); >+ void read(const Vector<uint8_t>&); >+ void returnMessage(Optional<fido::FidoHidMessage>&&); >+ >+ UniqueRef<HidConnection> m_connection; >+ State m_state { State::Idle }; >+ Optional<fido::FidoHidMessage> m_requestMessage; >+ Optional<fido::FidoHidMessage> m_responseMessage; >+ MessageCallback m_callback; >+ }; >+ >+ void continueAfterChannelAllocated(Optional<fido::FidoHidMessage>&&); >+ void continueAfterResponseReceived(Optional<fido::FidoHidMessage>&&); >+ void returnResponse(Vector<uint8_t>&&); >+ >+ UniqueRef<Worker> m_worker; >+ State m_state { State::Idle }; >+ uint32_t m_channelId { fido::kHidBroadcastChannel }; >+ // One request at a time. >+ Vector<uint8_t> m_requestData; >+ ResponseCallback m_responseCallback; >+ Vector<uint8_t> m_nonce; >+ fido::ProtocolVersion m_protocol { fido::ProtocolVersion::kCtap }; >+}; >+ >+} // namespace WebKit >+ >+#endif // ENABLE(WEB_AUTHN) && PLATFORM(MAC) >diff --git a/Source/WebKit/UIProcess/WebAuthentication/Fido/U2fHidAuthenticator.cpp b/Source/WebKit/UIProcess/WebAuthentication/Fido/U2fHidAuthenticator.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..8447f42f63b38aead53ee9b57c45fb3b28ebfb12 >--- /dev/null >+++ b/Source/WebKit/UIProcess/WebAuthentication/Fido/U2fHidAuthenticator.cpp >@@ -0,0 +1,222 @@ >+/* >+ * 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. >+ */ >+ >+#include "config.h" >+#include "U2fHidAuthenticator.h" >+ >+#if ENABLE(WEB_AUTHN) && PLATFORM(MAC) >+ >+#include "CtapHidDriver.h" >+#include <WebCore/ApduResponse.h> >+#include <WebCore/ExceptionData.h> >+#include <WebCore/U2fCommandConstructor.h> >+#include <WebCore/U2fResponseConverter.h> >+#include <wtf/RunLoop.h> >+#include <wtf/text/StringConcatenateNumbers.h> >+ >+namespace WebKit { >+using namespace WebCore; >+using namespace apdu; >+using namespace fido; >+ >+namespace { >+const unsigned retryTimeOutValueMs = 200; >+} >+ >+U2fHidAuthenticator::U2fHidAuthenticator(std::unique_ptr<CtapHidDriver>&& driver) >+ : m_driver(WTFMove(driver)) >+ , m_retryTimer(RunLoop::main(), this, &U2fHidAuthenticator::retryLastCommand) >+{ >+ // FIXME(191520): We need a way to convert std::unique_ptr to UniqueRef. >+ ASSERT(m_driver); >+} >+ >+void U2fHidAuthenticator::makeCredential() >+{ >+ if (!isConvertibleToU2fRegisterCommand(requestData().creationOptions)) { >+ receiveRespond(ExceptionData { NotSupportedError, "Cannot convert the request to U2F command."_s }); >+ return; >+ } >+ if (!requestData().creationOptions.excludeCredentials.isEmpty()) { >+ ASSERT(!m_nextListIndex); >+ checkExcludeList(m_nextListIndex++); >+ return; >+ } >+ issueRegisterCommand(); >+} >+ >+void U2fHidAuthenticator::checkExcludeList(size_t index) >+{ >+ if (index >= requestData().creationOptions.excludeCredentials.size()) { >+ issueRegisterCommand(); >+ return; >+ } >+ auto u2fCmd = convertToU2fCheckOnlySignCommand(requestData().hash, requestData().creationOptions, requestData().creationOptions.excludeCredentials[index]); >+ ASSERT(u2fCmd); >+ issueNewCommand(WTFMove(*u2fCmd), CommandType::CheckOnlyCommand); >+} >+ >+void U2fHidAuthenticator::issueRegisterCommand() >+{ >+ auto u2fCmd = convertToU2fRegisterCommand(requestData().hash, requestData().creationOptions); >+ ASSERT(u2fCmd); >+ issueNewCommand(WTFMove(*u2fCmd), CommandType::RegisterCommand); >+} >+ >+void U2fHidAuthenticator::getAssertion() >+{ >+ if (!isConvertibleToU2fSignCommand(requestData().requestOptions)) { >+ receiveRespond(ExceptionData { NotSupportedError, "Cannot convert the request to U2F command."_s }); >+ return; >+ } >+ ASSERT(!m_nextListIndex); >+ issueSignCommand(m_nextListIndex++); >+} >+ >+void U2fHidAuthenticator::issueSignCommand(size_t index) >+{ >+ if (index >= requestData().requestOptions.allowCredentials.size()) { >+ receiveRespond(ExceptionData { NotAllowedError, "No credentials from the allowCredentials list is found in the authenticator."_s }); >+ return; >+ } >+ auto u2fCmd = convertToU2fSignCommand(requestData().hash, requestData().requestOptions, requestData().requestOptions.allowCredentials[index].idVector); >+ ASSERT(u2fCmd); >+ issueNewCommand(WTFMove(*u2fCmd), CommandType::SignCommand); >+} >+ >+void U2fHidAuthenticator::issueNewCommand(Vector<uint8_t>&& command, CommandType type) >+{ >+ m_lastCommand = WTFMove(command); >+ m_lastCommandType = type; >+ issueCommand(m_lastCommand, m_lastCommandType); >+} >+ >+void U2fHidAuthenticator::issueCommand(const Vector<uint8_t>& command, CommandType type) >+{ >+ m_driver->transact(Vector<uint8_t>(command), [weakThis = makeWeakPtr(*this), type](Vector<uint8_t>&& data) { >+ ASSERT(RunLoop::isMain()); >+ if (!weakThis) >+ return; >+ weakThis->responseReceived(WTFMove(data), type); >+ }); >+} >+ >+void U2fHidAuthenticator::responseReceived(Vector<uint8_t>&& response, CommandType type) >+{ >+ auto apduResponse = ApduResponse::createFromMessage(response); >+ if (!apduResponse) { >+ receiveRespond(ExceptionData { UnknownError, "Couldn't parse the APDU response."_s }); >+ return; >+ } >+ >+ switch (type) { >+ case CommandType::RegisterCommand: >+ continueRegisterCommandAfterResponseReceived(WTFMove(*apduResponse)); >+ return; >+ case CommandType::CheckOnlyCommand: >+ continueCheckOnlyCommandAfterResponseReceived(WTFMove(*apduResponse)); >+ return; >+ case CommandType::BogusCommand: >+ continueBogusCommandAfterResponseReceived(WTFMove(*apduResponse)); >+ return; >+ case CommandType::SignCommand: >+ continueSignCommandAfterResponseReceived(WTFMove(*apduResponse)); >+ return; >+ } >+ ASSERT_NOT_REACHED(); >+} >+ >+void U2fHidAuthenticator::continueRegisterCommandAfterResponseReceived(ApduResponse&& apduResponse) >+{ >+ switch (apduResponse.status()) { >+ case ApduResponse::Status::SW_NO_ERROR: { >+ auto response = readU2fRegisterResponse(requestData().creationOptions.rp.id, apduResponse.data()); >+ if (!response) { >+ receiveRespond(ExceptionData { UnknownError, "Couldn't parse the U2F register response."_s }); >+ return; >+ } >+ receiveRespond(WTFMove(*response)); >+ return; >+ } >+ case ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED: >+ // Polling is required during test of user presence. >+ m_retryTimer.startOneShot(Seconds::fromMilliseconds(retryTimeOutValueMs)); >+ return; >+ default: >+ receiveRespond(ExceptionData { UnknownError, makeString("Unknown internal error. Error code: ", static_cast<unsigned>(apduResponse.status())) }); >+ } >+} >+ >+void U2fHidAuthenticator::continueCheckOnlyCommandAfterResponseReceived(ApduResponse&& apduResponse) >+{ >+ switch (apduResponse.status()) { >+ case ApduResponse::Status::SW_NO_ERROR: >+ case ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED: >+ issueNewCommand(constructBogusU2fRegistrationCommand(), CommandType::BogusCommand); >+ return; >+ default: >+ checkExcludeList(m_nextListIndex++); >+ } >+} >+ >+void U2fHidAuthenticator::continueBogusCommandAfterResponseReceived(ApduResponse&& apduResponse) >+{ >+ switch (apduResponse.status()) { >+ case ApduResponse::Status::SW_NO_ERROR: >+ receiveRespond(ExceptionData { InvalidStateError, "At least one credential matches an entry of the excludeCredentials list in the authenticator."_s }); >+ return; >+ case ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED: >+ // Polling is required during test of user presence. >+ m_retryTimer.startOneShot(Seconds::fromMilliseconds(retryTimeOutValueMs)); >+ return; >+ default: >+ receiveRespond(ExceptionData { UnknownError, makeString("Unknown internal error. Error code: ", static_cast<unsigned>(apduResponse.status())) }); >+ } >+} >+ >+void U2fHidAuthenticator::continueSignCommandAfterResponseReceived(ApduResponse&& apduResponse) >+{ >+ switch (apduResponse.status()) { >+ case ApduResponse::Status::SW_NO_ERROR: { >+ auto response = readU2fSignResponse(requestData().requestOptions.rpId, requestData().requestOptions.allowCredentials[m_nextListIndex - 1].idVector, apduResponse.data()); >+ if (!response) { >+ receiveRespond(ExceptionData { UnknownError, "Couldn't parse the U2F sign response."_s }); >+ return; >+ } >+ receiveRespond(WTFMove(*response)); >+ return; >+ } >+ case ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED: >+ // Polling is required during test of user presence. >+ m_retryTimer.startOneShot(Seconds::fromMilliseconds(retryTimeOutValueMs)); >+ return; >+ default: >+ issueSignCommand(m_nextListIndex++); >+ } >+} >+ >+} // namespace WebKit >+ >+#endif // ENABLE(WEB_AUTHN) && PLATFORM(MAC) >diff --git a/Source/WebKit/UIProcess/WebAuthentication/Fido/U2fHidAuthenticator.h b/Source/WebKit/UIProcess/WebAuthentication/Fido/U2fHidAuthenticator.h >new file mode 100644 >index 0000000000000000000000000000000000000000..7a6f19fdd6859326408e9ef651cc1c1cc7527b7e >--- /dev/null >+++ b/Source/WebKit/UIProcess/WebAuthentication/Fido/U2fHidAuthenticator.h >@@ -0,0 +1,80 @@ >+/* >+ * 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) && PLATFORM(MAC) >+ >+#include "Authenticator.h" >+ >+namespace apdu { >+class ApduResponse; >+} >+ >+namespace WebKit { >+ >+class CtapHidDriver; >+ >+class U2fHidAuthenticator final : public Authenticator { >+public: >+ static Ref<U2fHidAuthenticator> create(std::unique_ptr<CtapHidDriver>&& driver) >+ { >+ return adoptRef(*new U2fHidAuthenticator(WTFMove(driver))); >+ } >+ >+private: >+ explicit U2fHidAuthenticator(std::unique_ptr<CtapHidDriver>&&); >+ >+ void makeCredential() final; >+ void checkExcludeList(size_t index); >+ void issueRegisterCommand(); >+ void getAssertion() final; >+ void issueSignCommand(size_t index); >+ >+ enum class CommandType : uint8_t { >+ RegisterCommand, >+ CheckOnlyCommand, >+ BogusCommand, >+ SignCommand >+ }; >+ void issueNewCommand(Vector<uint8_t>&& command, CommandType); >+ void retryLastCommand() { issueCommand(m_lastCommand, m_lastCommandType); } >+ void issueCommand(const Vector<uint8_t>& command, CommandType); >+ void responseReceived(Vector<uint8_t>&& response, CommandType); >+ void continueRegisterCommandAfterResponseReceived(apdu::ApduResponse&&); >+ void continueCheckOnlyCommandAfterResponseReceived(apdu::ApduResponse&&); >+ void continueBogusCommandAfterResponseReceived(apdu::ApduResponse&&); >+ void continueSignCommandAfterResponseReceived(apdu::ApduResponse&&); >+ >+ std::unique_ptr<CtapHidDriver> m_driver; >+ RunLoop::Timer<U2fHidAuthenticator> m_retryTimer; >+ Vector<uint8_t> m_lastCommand; >+ CommandType m_lastCommandType; >+ size_t m_nextListIndex { 0 }; >+}; >+ >+} // namespace WebKit >+ >+#endif // ENABLE(WEB_AUTHN) && PLATFORM(MAC) >diff --git a/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidConnection.cpp b/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidConnection.cpp >index 0b17699bdde9784b4c7863d9d7be9245465f2a64..d86a3a472e6bb77390e3401a6cde82c653a6846e 100644 >--- a/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidConnection.cpp >+++ b/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidConnection.cpp >@@ -127,43 +127,48 @@ void MockHidConnection::parseRequest() > if (previousSubStage == Mock::SubStage::Msg) > m_stage = Mock::Stage::Request; > } >- if (m_requestMessage->cmd() == FidoHidDeviceCommand::kCbor) >+ if (m_requestMessage->cmd() == FidoHidDeviceCommand::kCbor || m_requestMessage->cmd() == FidoHidDeviceCommand::kMsg) > m_subStage = Mock::SubStage::Msg; > >- // Set options. > if (m_stage == Mock::Stage::Request && m_subStage == Mock::SubStage::Msg) { >- m_requireResidentKey = false; >- m_requireUserVerification = false; >- >- auto payload = m_requestMessage->getMessagePayload(); >- ASSERT(payload.size()); >- auto cmd = static_cast<CtapRequestCommand>(payload[0]); >- payload.remove(0); >- auto requestMap = CBORReader::read(payload); >- ASSERT(requestMap); >- >- if (cmd == CtapRequestCommand::kAuthenticatorMakeCredential) { >- auto it = requestMap->getMap().find(CBORValue(CtapMakeCredentialRequestOptionsKey)); // Find options. >- if (it != requestMap->getMap().end()) { >- auto& optionMap = it->second.getMap(); >- >- auto itr = optionMap.find(CBORValue(kResidentKeyMapKey)); >- if (itr != optionMap.end()) >- m_requireResidentKey = itr->second.getBool(); >- >- itr = optionMap.find(CBORValue(kUserVerificationMapKey)); >- if (itr != optionMap.end()) >- m_requireUserVerification = itr->second.getBool(); >+ // Make sure we issue different msg cmd for CTAP and U2F. >+ ASSERT(m_configuration.hid->isU2f ^ (m_requestMessage->cmd() != FidoHidDeviceCommand::kMsg)); >+ >+ // Set options. >+ if (m_requestMessage->cmd() == FidoHidDeviceCommand::kCbor) { >+ m_requireResidentKey = false; >+ m_requireUserVerification = false; >+ >+ auto payload = m_requestMessage->getMessagePayload(); >+ ASSERT(payload.size()); >+ auto cmd = static_cast<CtapRequestCommand>(payload[0]); >+ payload.remove(0); >+ auto requestMap = CBORReader::read(payload); >+ ASSERT(requestMap); >+ >+ if (cmd == CtapRequestCommand::kAuthenticatorMakeCredential) { >+ auto it = requestMap->getMap().find(CBORValue(CtapMakeCredentialRequestOptionsKey)); // Find options. >+ if (it != requestMap->getMap().end()) { >+ auto& optionMap = it->second.getMap(); >+ >+ auto itr = optionMap.find(CBORValue(kResidentKeyMapKey)); >+ if (itr != optionMap.end()) >+ m_requireResidentKey = itr->second.getBool(); >+ >+ itr = optionMap.find(CBORValue(kUserVerificationMapKey)); >+ if (itr != optionMap.end()) >+ m_requireUserVerification = itr->second.getBool(); >+ } > } >- } > >- if (cmd == CtapRequestCommand::kAuthenticatorGetAssertion) { >- auto it = requestMap->getMap().find(CBORValue(CtapGetAssertionRequestOptionsKey)); // Find options. >- if (it != requestMap->getMap().end()) { >- auto& optionMap = it->second.getMap(); >- auto itr = optionMap.find(CBORValue(kUserVerificationMapKey)); >- if (itr != optionMap.end()) >- m_requireUserVerification = itr->second.getBool(); >+ if (cmd == CtapRequestCommand::kAuthenticatorGetAssertion) { >+ auto it = requestMap->getMap().find(CBORValue(CtapGetAssertionRequestOptionsKey)); // Find options. >+ if (it != requestMap->getMap().end()) { >+ auto& optionMap = it->second.getMap(); >+ auto itr = optionMap.find(CBORValue(kUserVerificationMapKey)); >+ if (itr != optionMap.end()) >+ m_requireUserVerification = itr->second.getBool(); >+ } > } > } > } >@@ -207,8 +212,12 @@ void MockHidConnection::feedReports() > infoData.insert(0, static_cast<uint8_t>(CtapDeviceResponseCode::kSuccess)); // Prepend status code. > if (stagesMatch() && m_configuration.hid->error == Mock::Error::WrongChannelId) > message = FidoHidMessage::create(m_currentChannel - 1, FidoHidDeviceCommand::kCbor, infoData); >- else >- message = FidoHidMessage::create(m_currentChannel, FidoHidDeviceCommand::kCbor, infoData); >+ else { >+ if (!m_configuration.hid->isU2f) >+ message = FidoHidMessage::create(m_currentChannel, FidoHidDeviceCommand::kCbor, infoData); >+ else >+ message = FidoHidMessage::create(m_currentChannel, FidoHidDeviceCommand::kError, { static_cast<uint8_t>(CtapDeviceResponseCode::kCtap1ErrInvalidCommand) }); >+ } > } > > if (m_stage == Mock::Stage::Request && m_subStage == Mock::SubStage::Msg) { >@@ -223,9 +232,14 @@ void MockHidConnection::feedReports() > message = FidoHidMessage::create(m_currentChannel, FidoHidDeviceCommand::kCbor, { static_cast<uint8_t>(CtapDeviceResponseCode::kCtap2ErrUnsupportedOption) }); > else { > Vector<uint8_t> payload; >- auto status = base64Decode(m_configuration.hid->payloadBase64, payload); >+ ASSERT(!m_configuration.hid->payloadBase64.isEmpty()); >+ auto status = base64Decode(m_configuration.hid->payloadBase64[0], payload); >+ m_configuration.hid->payloadBase64.remove(0); > ASSERT_UNUSED(status, status); >- message = FidoHidMessage::create(m_currentChannel, FidoHidDeviceCommand::kCbor, payload); >+ if (!m_configuration.hid->isU2f) >+ message = FidoHidMessage::create(m_currentChannel, FidoHidDeviceCommand::kCbor, payload); >+ else >+ message = FidoHidMessage::create(m_currentChannel, FidoHidDeviceCommand::kMsg, payload); > } > } > >diff --git a/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidConnection.h b/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidConnection.h >index e0bf86b2fd1e265bfc0276819e9ebcdcc1133948..3dcabc4b5dc970570fb5ec3ff91368bd4ebffe66 100644 >--- a/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidConnection.h >+++ b/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidConnection.h >@@ -35,11 +35,13 @@ > namespace WebKit { > > // The following basically simulates an external HID token that: >-// 1. Only supports CTAP2 protocol, >+// 1. Supports only one protocol, either CTAP2 or U2F. > // 2. Doesn't support resident keys, > // 3. Doesn't support user verification. >-// There are four stages for each WebAuthN request: >+// There are four stages for each CTAP request: > // FSM: Info::Init => Info::Msg => Request::Init => Request::Msg >+// There are indefinite stages for each U2F request: >+// FSM: Info::Init => Info::Msg => [Request::Init => Request::Msg]+ > // According to different combinations of error and stages, error will manifest differently. > class MockHidConnection final : public CanMakeWeakPtr<MockHidConnection>, public HidConnection { > public: >diff --git a/Source/WebKit/UIProcess/WebAuthentication/Mock/MockWebAuthenticationConfiguration.h b/Source/WebKit/UIProcess/WebAuthentication/Mock/MockWebAuthenticationConfiguration.h >index 78867a5fc666b97db9070294e4ef9bcd0d82237c..be0872fbdfb9cac82783b545f54996d64ee79376 100644 >--- a/Source/WebKit/UIProcess/WebAuthentication/Mock/MockWebAuthenticationConfiguration.h >+++ b/Source/WebKit/UIProcess/WebAuthentication/Mock/MockWebAuthenticationConfiguration.h >@@ -61,10 +61,11 @@ struct MockWebAuthenticationConfiguration { > WrongNonce > }; > >- String payloadBase64; >+ Vector<String> payloadBase64; > Stage stage { Stage::Info }; > SubStage subStage { SubStage::Init }; > Error error { Error::Success }; >+ bool isU2f { false }; > bool keepAlive { false }; > bool fastDataArrival { false }; > bool continueAfterErrorData { false }; >diff --git a/Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidAuthenticator.cpp b/Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidAuthenticator.cpp >deleted file mode 100644 >index ab42af66f91079a197d5c3d3bc872be2a3577126..0000000000000000000000000000000000000000 >--- a/Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidAuthenticator.cpp >+++ /dev/null >@@ -1,98 +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. >- */ >- >-#include "config.h" >-#include "CtapHidAuthenticator.h" >- >-#if ENABLE(WEB_AUTHN) && PLATFORM(MAC) >- >-#include "CtapHidDriver.h" >-#include <WebCore/DeviceRequestConverter.h> >-#include <WebCore/DeviceResponseConverter.h> >-#include <WebCore/ExceptionData.h> >-#include <wtf/RunLoop.h> >-#include <wtf/text/StringConcatenateNumbers.h> >- >-namespace WebKit { >-using namespace WebCore; >-using namespace fido; >- >-CtapHidAuthenticator::CtapHidAuthenticator(std::unique_ptr<CtapHidDriver>&& driver, AuthenticatorGetInfoResponse&& info) >- : m_driver(WTFMove(driver)) >- , m_info(WTFMove(info)) >-{ >- // FIXME(191520): We need a way to convert std::unique_ptr to UniqueRef. >- ASSERT(m_driver); >-} >- >-void CtapHidAuthenticator::makeCredential() >-{ >- // FIXME(192061) >- LOG_ERROR("Start making credentials."); >- auto cborCmd = encodeMakeCredenitalRequestAsCBOR(requestData().hash, requestData().creationOptions, m_info.options().userVerificationAvailability()); >- m_driver->transact(WTFMove(cborCmd), [weakThis = makeWeakPtr(*this)](Vector<uint8_t>&& data) { >- ASSERT(RunLoop::isMain()); >- if (!weakThis) >- return; >- weakThis->continueMakeCredentialAfterResponseReceived(WTFMove(data)); >- }); >-} >- >-void CtapHidAuthenticator::continueMakeCredentialAfterResponseReceived(Vector<uint8_t>&& data) const >-{ >- auto response = readCTAPMakeCredentialResponse(data); >- if (!response) { >- receiveRespond(ExceptionData { UnknownError, makeString("Unknown internal error. Error code: ", data.size() == 1 ? data[0] : -1) }); >- return; >- } >- receiveRespond(WTFMove(*response)); >-} >- >-void CtapHidAuthenticator::getAssertion() >-{ >- // FIXME(192061) >- LOG_ERROR("Start getting assertions."); >- auto cborCmd = encodeGetAssertionRequestAsCBOR(requestData().hash, requestData().requestOptions, m_info.options().userVerificationAvailability()); >- m_driver->transact(WTFMove(cborCmd), [weakThis = makeWeakPtr(*this)](Vector<uint8_t>&& data) { >- ASSERT(RunLoop::isMain()); >- if (!weakThis) >- return; >- weakThis->continueGetAssertionAfterResponseReceived(WTFMove(data)); >- }); >-} >- >-void CtapHidAuthenticator::continueGetAssertionAfterResponseReceived(Vector<uint8_t>&& data) const >-{ >- auto response = readCTAPGetAssertionResponse(data); >- if (!response) { >- receiveRespond(ExceptionData { UnknownError, makeString("Unknown internal error. Error code: ", data.size() == 1 ? data[0] : -1) }); >- return; >- } >- receiveRespond(WTFMove(*response)); >-} >- >-} // namespace WebKit >- >-#endif // ENABLE(WEB_AUTHN) && PLATFORM(MAC) >diff --git a/Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidAuthenticator.h b/Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidAuthenticator.h >deleted file mode 100644 >index fdf6279525691dec5fa8d356e0ae19f2bf207b66..0000000000000000000000000000000000000000 >--- a/Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidAuthenticator.h >+++ /dev/null >@@ -1,58 +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. >- */ >- >-#pragma once >- >-#if ENABLE(WEB_AUTHN) && PLATFORM(MAC) >- >-#include "Authenticator.h" >-#include <WebCore/AuthenticatorGetInfoResponse.h> >- >-namespace WebKit { >- >-class CtapHidDriver; >- >-class CtapHidAuthenticator final : public Authenticator { >-public: >- static Ref<CtapHidAuthenticator> create(std::unique_ptr<CtapHidDriver>&& driver, fido::AuthenticatorGetInfoResponse&& info) >- { >- return adoptRef(*new CtapHidAuthenticator(WTFMove(driver), WTFMove(info))); >- } >- >-private: >- explicit CtapHidAuthenticator(std::unique_ptr<CtapHidDriver>&&, fido::AuthenticatorGetInfoResponse&&); >- >- void makeCredential() final; >- void continueMakeCredentialAfterResponseReceived(Vector<uint8_t>&&) const; >- void getAssertion() final; >- void continueGetAssertionAfterResponseReceived(Vector<uint8_t>&&) const; >- >- std::unique_ptr<CtapHidDriver> m_driver; >- fido::AuthenticatorGetInfoResponse m_info; >-}; >- >-} // namespace WebKit >- >-#endif // ENABLE(WEB_AUTHN) && PLATFORM(MAC) >diff --git a/Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidDriver.cpp b/Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidDriver.cpp >deleted file mode 100644 >index 6f2862032bc5b64e309c74d76d98c20b256272a8..0000000000000000000000000000000000000000 >--- a/Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidDriver.cpp >+++ /dev/null >@@ -1,228 +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. >- */ >- >-#include "config.h" >-#include "CtapHidDriver.h" >- >-#if ENABLE(WEB_AUTHN) && PLATFORM(MAC) >- >-#include <WebCore/FidoConstants.h> >-#include <wtf/CryptographicallyRandomNumber.h> >-#include <wtf/RunLoop.h> >-#include <wtf/Vector.h> >-#include <wtf/text/Base64.h> >- >-namespace WebKit { >-using namespace fido; >- >-CtapHidDriver::Worker::Worker(UniqueRef<HidConnection>&& connection) >- : m_connection(WTFMove(connection)) >-{ >- m_connection->initialize(); >-} >- >-CtapHidDriver::Worker::~Worker() >-{ >- m_connection->terminate(); >-} >- >-void CtapHidDriver::Worker::transact(fido::FidoHidMessage&& requestMessage, MessageCallback&& callback) >-{ >- ASSERT(m_state == State::Idle); >- m_state = State::Write; >- m_requestMessage = WTFMove(requestMessage); >- m_responseMessage.reset(); >- m_callback = WTFMove(callback); >- >- // HidConnection could hold data from other applications, and thereofore invalidate it before each transaction. >- m_connection->invalidateCache(); >- m_connection->send(m_requestMessage->popNextPacket(), [weakThis = makeWeakPtr(*this)](HidConnection::DataSent sent) mutable { >- ASSERT(RunLoop::isMain()); >- if (!weakThis) >- return; >- weakThis->write(sent); >- }); >-} >- >-void CtapHidDriver::Worker::write(HidConnection::DataSent sent) >-{ >- ASSERT(m_state == State::Write); >- // FIXME(192061) >- LOG_ERROR("Start writing data."); >- if (sent != HidConnection::DataSent::Yes) { >- returnMessage(WTF::nullopt); >- return; >- } >- >- if (!m_requestMessage->numPackets()) { >- m_state = State::Read; >- m_connection->registerDataReceivedCallback([weakThis = makeWeakPtr(*this)](Vector<uint8_t>&& data) mutable { >- ASSERT(RunLoop::isMain()); >- if (!weakThis) >- return; >- weakThis->read(data); >- }); >- return; >- } >- >- m_connection->send(m_requestMessage->popNextPacket(), [weakThis = makeWeakPtr(*this)](HidConnection::DataSent sent) mutable { >- ASSERT(RunLoop::isMain()); >- if (!weakThis) >- return; >- weakThis->write(sent); >- }); >-} >- >-void CtapHidDriver::Worker::read(const Vector<uint8_t>& data) >-{ >- ASSERT(m_state == State::Read); >- // FIXME(192061) >- LOG_ERROR("Start reading data."); >- if (!m_responseMessage) { >- m_responseMessage = FidoHidMessage::createFromSerializedData(data); >- // The first few reports could be for other applications, and therefore ignore those. >- if (!m_responseMessage || m_responseMessage->channelId() != m_requestMessage->channelId()) { >- LOG_ERROR("Couldn't parse a hid init packet: %s", m_responseMessage ? "wrong channel id." : "bad data."); >- m_responseMessage.reset(); >- return; >- } >- } else { >- if (!m_responseMessage->addContinuationPacket(data)) { >- LOG_ERROR("Couldn't parse a hid continuation packet."); >- returnMessage(WTF::nullopt); >- return; >- } >- } >- >- if (m_responseMessage->messageComplete()) { >- // A KeepAlive cmd could be sent between a request and a response to indicate that >- // the authenticator is waiting for user consent. Keep listening for the response. >- if (m_responseMessage->cmd() == FidoHidDeviceCommand::kKeepAlive) { >- m_responseMessage.reset(); >- return; >- } >- returnMessage(WTFMove(m_responseMessage)); >- return; >- } >-} >- >-void CtapHidDriver::Worker::returnMessage(Optional<fido::FidoHidMessage>&& message) >-{ >- // FIXME(192061) >- LOG_ERROR("Start returning data."); >- m_state = State::Idle; >- m_connection->unregisterDataReceivedCallback(); >- m_callback(WTFMove(message)); >-} >- >-CtapHidDriver::CtapHidDriver(UniqueRef<HidConnection>&& connection) >- : m_worker(makeUniqueRef<Worker>(WTFMove(connection))) >- , m_nonce(kHidInitNonceLength) >-{ >-} >- >-void CtapHidDriver::transact(Vector<uint8_t>&& data, ResponseCallback&& callback) >-{ >- ASSERT(m_state == State::Idle); >- m_state = State::AllocateChannel; >- m_channelId = kHidBroadcastChannel; >- m_requestData = WTFMove(data); >- m_responseCallback = WTFMove(callback); >- >- // Allocate a channel. >- // FIXME(192061) >- LOG_ERROR("Start allocating a channel."); >- ASSERT(m_nonce.size() == kHidInitNonceLength); >- cryptographicallyRandomValues(m_nonce.data(), m_nonce.size()); >- auto initCommand = FidoHidMessage::create(m_channelId, FidoHidDeviceCommand::kInit, m_nonce); >- ASSERT(initCommand); >- m_worker->transact(WTFMove(*initCommand), [weakThis = makeWeakPtr(*this)](Optional<FidoHidMessage>&& response) mutable { >- ASSERT(RunLoop::isMain()); >- if (!weakThis) >- return; >- weakThis->continueAfterChannelAllocated(WTFMove(response)); >- }); >-} >- >-void CtapHidDriver::continueAfterChannelAllocated(Optional<FidoHidMessage>&& message) >-{ >- ASSERT(m_state == State::AllocateChannel); >- if (!message) { >- returnResponse({ }); >- return; >- } >- ASSERT(message->channelId() == m_channelId); >- >- auto payload = message->getMessagePayload(); >- ASSERT(payload.size() == kHidInitResponseSize); >- // Restart the transaction in the next run loop when nonce mismatches. >- if (memcmp(payload.data(), m_nonce.data(), m_nonce.size())) { >- m_state = State::Idle; >- RunLoop::main().dispatch([weakThis = makeWeakPtr(*this), data = WTFMove(m_requestData), callback = WTFMove(m_responseCallback)]() mutable { >- if (!weakThis) >- return; >- weakThis->transact(WTFMove(data), WTFMove(callback)); >- }); >- return; >- } >- >- m_state = State::Ready; >- auto index = kHidInitNonceLength; >- m_channelId = static_cast<uint32_t>(payload[index++]) << 24; >- 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(192061) >- LOG_ERROR("Start sending the request."); >- auto cmd = FidoHidMessage::create(m_channelId, FidoHidDeviceCommand::kCbor, m_requestData); >- ASSERT(cmd); >- m_worker->transact(WTFMove(*cmd), [weakThis = makeWeakPtr(*this)](Optional<FidoHidMessage>&& response) mutable { >- ASSERT(RunLoop::isMain()); >- if (!weakThis) >- return; >- weakThis->continueAfterResponseReceived(WTFMove(response)); >- }); >-} >- >-void CtapHidDriver::continueAfterResponseReceived(Optional<fido::FidoHidMessage>&& message) >-{ >- ASSERT(m_state == State::Ready); >- ASSERT(!message || message->channelId() == m_channelId); >- // FIXME(192061) >- LOG_ERROR("Start returning the response."); >- returnResponse(message ? message->getMessagePayload() : Vector<uint8_t>()); >-} >- >-void CtapHidDriver::returnResponse(Vector<uint8_t>&& response) >-{ >- // Reset state before calling the response callback to avoid being deleted. >- m_state = State::Idle; >- m_responseCallback(WTFMove(response)); >-} >- >-} // namespace WebKit >- >-#endif // ENABLE(WEB_AUTHN) && PLATFORM(MAC) >diff --git a/Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidDriver.h b/Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidDriver.h >deleted file mode 100644 >index de05026894fefe1e953e039d0cc88f346db5a426..0000000000000000000000000000000000000000 >--- a/Source/WebKit/UIProcess/WebAuthentication/fido/CtapHidDriver.h >+++ /dev/null >@@ -1,110 +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. >- */ >- >-#pragma once >- >-#if ENABLE(WEB_AUTHN) && PLATFORM(MAC) >- >-#include "HidConnection.h" >-#include <WebCore/FidoConstants.h> >-#include <WebCore/FidoHidMessage.h> >-#include <wtf/CompletionHandler.h> >-#include <wtf/Forward.h> >-#include <wtf/Noncopyable.h> >-#include <wtf/UniqueRef.h> >-#include <wtf/WeakPtr.h> >- >-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 CanMakeWeakPtr<CtapHidDriver> { >- WTF_MAKE_FAST_ALLOCATED; >- WTF_MAKE_NONCOPYABLE(CtapHidDriver); >-public: >- using ResponseCallback = Function<void(Vector<uint8_t>&&)>; >- >- enum class State : uint8_t { >- Idle, >- AllocateChannel, >- Ready, >- // FIXME(191528) >- Busy >- }; >- >- explicit CtapHidDriver(UniqueRef<HidConnection>&&); >- >- void transact(Vector<uint8_t>&& data, ResponseCallback&&); >- >-private: >- // Worker is the helper that maintains the transaction. >- // https://fidoalliance.org/specs/fido-v2.0-ps-20170927/fido-client-to-authenticator-protocol-v2.0-ps-20170927.html#arbitration >- // FSM: Idle => Write => Read. >- class Worker : public CanMakeWeakPtr<Worker> { >- WTF_MAKE_FAST_ALLOCATED; >- WTF_MAKE_NONCOPYABLE(Worker); >- public: >- using MessageCallback = Function<void(Optional<fido::FidoHidMessage>&&)>; >- >- enum class State : uint8_t { >- Idle, >- Write, >- Read >- }; >- >- explicit Worker(UniqueRef<HidConnection>&&); >- ~Worker(); >- >- void transact(fido::FidoHidMessage&&, MessageCallback&&); >- >- private: >- void write(HidConnection::DataSent); >- void read(const Vector<uint8_t>&); >- void returnMessage(Optional<fido::FidoHidMessage>&&); >- >- UniqueRef<HidConnection> m_connection; >- State m_state { State::Idle }; >- Optional<fido::FidoHidMessage> m_requestMessage; >- Optional<fido::FidoHidMessage> m_responseMessage; >- MessageCallback m_callback; >- }; >- >- void continueAfterChannelAllocated(Optional<fido::FidoHidMessage>&&); >- void continueAfterResponseReceived(Optional<fido::FidoHidMessage>&&); >- void returnResponse(Vector<uint8_t>&&); >- >- UniqueRef<Worker> m_worker; >- State m_state { State::Idle }; >- uint32_t m_channelId { fido::kHidBroadcastChannel }; >- // One request at a time. >- Vector<uint8_t> m_requestData; >- ResponseCallback m_responseCallback; >- Vector<uint8_t> m_nonce; >-}; >- >-} // namespace WebKit >- >-#endif // ENABLE(WEB_AUTHN) && PLATFORM(MAC) >diff --git a/Source/WebKit/WebKit.xcodeproj/project.pbxproj b/Source/WebKit/WebKit.xcodeproj/project.pbxproj >index 00a1c5f6499a9bda2609a200bb30147f7cd6fb34..6c2e7a2e4990cdab878ea511c6b50a80500aeb4a 100644 >--- a/Source/WebKit/WebKit.xcodeproj/project.pbxproj >+++ b/Source/WebKit/WebKit.xcodeproj/project.pbxproj >@@ -1015,8 +1015,6 @@ > 570AB8F320AE3BD700B8BE87 /* SecKeyProxyStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 570AB8F220AE3BD700B8BE87 /* SecKeyProxyStore.h */; }; > 57597EB921811D9A0037F924 /* CtapHidDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 57597EB721811D9A0037F924 /* CtapHidDriver.h */; }; > 57597EBD218184900037F924 /* CtapHidAuthenticator.h in Headers */ = {isa = PBXBuildFile; fileRef = 57597EBB2181848F0037F924 /* CtapHidAuthenticator.h */; }; >- 57597EBE218184900037F924 /* CtapHidAuthenticator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 57597EBC2181848F0037F924 /* CtapHidAuthenticator.cpp */; }; >- 57597EC121818BE20037F924 /* CtapHidDriver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 57597EC021818BE20037F924 /* CtapHidDriver.cpp */; }; > 5772F206217DBD6A0056BF2C /* HidService.h in Headers */ = {isa = PBXBuildFile; fileRef = 5772F204217DBD6A0056BF2C /* HidService.h */; }; > 578DC2982155A0020074E815 /* LocalAuthenticationSoftLink.h in Headers */ = {isa = PBXBuildFile; fileRef = 578DC2972155A0010074E815 /* LocalAuthenticationSoftLink.h */; }; > 57AC8F50217FEED90055438C /* HidConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 57AC8F4E217FEED90055438C /* HidConnection.h */; }; >@@ -1038,6 +1036,7 @@ > 57DCEDC3214F114C0016B847 /* MockLocalService.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DCEDC1214F114C0016B847 /* MockLocalService.h */; }; > 57DCEDC7214F18300016B847 /* MockLocalConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DCEDC5214F18300016B847 /* MockLocalConnection.h */; }; > 57DCEDCB214F4E420016B847 /* MockAuthenticatorManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DCEDC9214F4E420016B847 /* MockAuthenticatorManager.h */; }; >+ 57EB2E3A21E1983E00B89CDF /* U2fHidAuthenticator.h in Headers */ = {isa = PBXBuildFile; fileRef = 57EB2E3821E1983E00B89CDF /* U2fHidAuthenticator.h */; }; > 587743A621C30BBE00AE9084 /* HTTPSUpgradeList.db in Resources */ = {isa = PBXBuildFile; fileRef = 587743A421C30AD800AE9084 /* HTTPSUpgradeList.db */; }; > 58E977DF21C49A00005D92A6 /* NetworkHTTPSUpgradeChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = 58E977DD21C49A00005D92A6 /* NetworkHTTPSUpgradeChecker.h */; }; > 5C0B17781E7C880E00E9123C /* NetworkSocketStreamMessageReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5C0B17741E7C879C00E9123C /* NetworkSocketStreamMessageReceiver.cpp */; }; >@@ -3408,6 +3407,8 @@ > 57DCEDC6214F18300016B847 /* MockLocalConnection.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MockLocalConnection.mm; sourceTree = "<group>"; }; > 57DCEDC9214F4E420016B847 /* MockAuthenticatorManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockAuthenticatorManager.h; sourceTree = "<group>"; }; > 57DCEDCD214F51680016B847 /* MockAuthenticatorManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MockAuthenticatorManager.cpp; sourceTree = "<group>"; }; >+ 57EB2E3821E1983E00B89CDF /* U2fHidAuthenticator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = U2fHidAuthenticator.h; sourceTree = "<group>"; }; >+ 57EB2E3921E1983E00B89CDF /* U2fHidAuthenticator.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = U2fHidAuthenticator.cpp; sourceTree = "<group>"; }; > 587743A421C30AD800AE9084 /* HTTPSUpgradeList.db */ = {isa = PBXFileReference; lastKnownFileType = file; name = HTTPSUpgradeList.db; path = DerivedSources/WebKit2/HTTPSUpgradeList.db; sourceTree = BUILT_PRODUCTS_DIR; }; > 58E977DC21C499FE005D92A6 /* NetworkHTTPSUpgradeChecker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkHTTPSUpgradeChecker.cpp; sourceTree = "<group>"; }; > 58E977DD21C49A00005D92A6 /* NetworkHTTPSUpgradeChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkHTTPSUpgradeChecker.h; sourceTree = "<group>"; }; >@@ -5515,8 +5516,8 @@ > 2D1551A91F5A9B420006E3FE /* RemoteLayerTree */ = { > isa = PBXGroup; > children = ( >- E404906F21DE65D70037F0DB /* mac */, > 2D1551AA1F5A9BA70006E3FE /* ios */, >+ E404906F21DE65D70037F0DB /* mac */, > 1AB16AE01648656D00290D62 /* RemoteLayerTreeDrawingAreaProxy.h */, > 0FF24A2F1879E4FE003ABF0C /* RemoteLayerTreeDrawingAreaProxy.messages.in */, > 1AB16ADF1648656D00290D62 /* RemoteLayerTreeDrawingAreaProxy.mm */, >@@ -6704,15 +6705,17 @@ > name = Frameworks; > sourceTree = "<group>"; > }; >- 57597EBF218184B20037F924 /* fido */ = { >+ 57597EBF218184B20037F924 /* Fido */ = { > isa = PBXGroup; > children = ( > 57597EBC2181848F0037F924 /* CtapHidAuthenticator.cpp */, > 57597EBB2181848F0037F924 /* CtapHidAuthenticator.h */, > 57597EC021818BE20037F924 /* CtapHidDriver.cpp */, > 57597EB721811D9A0037F924 /* CtapHidDriver.h */, >+ 57EB2E3921E1983E00B89CDF /* U2fHidAuthenticator.cpp */, >+ 57EB2E3821E1983E00B89CDF /* U2fHidAuthenticator.h */, > ); >- path = fido; >+ path = Fido; > sourceTree = "<group>"; > }; > 5760828A202984C900116678 /* WebAuthentication */ = { >@@ -6729,7 +6732,7 @@ > isa = PBXGroup; > children = ( > 57DCED9E2148F9D10016B847 /* Cocoa */, >- 57597EBF218184B20037F924 /* fido */, >+ 57597EBF218184B20037F924 /* Fido */, > 57DCEDBD214C9FA90016B847 /* Mock */, > 57DCEDA42149E64A0016B847 /* Authenticator.cpp */, > 57DCED8B21485BD70016B847 /* Authenticator.h */, >@@ -9315,6 +9318,7 @@ > 1AAF263914687C39004A1E8A /* TiledCoreAnimationDrawingArea.h in Headers */, > 1AF05D8714688348008B1E81 /* TiledCoreAnimationDrawingAreaProxy.h in Headers */, > 2F8336861FA139DF00C6E080 /* TouchBarMenuData.h in Headers */, >+ 57EB2E3A21E1983E00B89CDF /* U2fHidAuthenticator.h in Headers */, > 1AFE436618B6C081009C7A48 /* UIDelegate.h in Headers */, > 515BE1B51D5917FF00DD7C68 /* UIGamepad.h in Headers */, > 515BE1A91D55293400DD7C68 /* UIGamepadProvider.h in Headers */, >@@ -10746,8 +10750,6 @@ > 2D92A786212B6AB100F493FD /* ChildProcess.cpp in Sources */, > 51FAEC3B1B0657680009C4E7 /* ChildProcessMessageReceiver.cpp in Sources */, > 2D92A77D212B6A7100F493FD /* Connection.cpp in Sources */, >- 57597EBE218184900037F924 /* CtapHidAuthenticator.cpp in Sources */, >- 57597EC121818BE20037F924 /* CtapHidDriver.cpp in Sources */, > 2D92A77E212B6A7100F493FD /* DataReference.cpp in Sources */, > 2D92A77F212B6A7100F493FD /* Decoder.cpp in Sources */, > 1AB7D6191288B9D900CFD08C /* DownloadProxyMessageReceiver.cpp in Sources */, >diff --git a/Tools/ChangeLog b/Tools/ChangeLog >index fb760ab7c36433be50fa4b87da2be8dad76e6bfb..4d56ad02a944b0f53edd128d058685a9ff354886 100644 >--- a/Tools/ChangeLog >+++ b/Tools/ChangeLog >@@ -1,3 +1,21 @@ >+2019-01-07 Jiewen Tan <jiewen_tan@apple.com> >+ >+ [WebAuthN] Support U2F HID Authenticators on macOS >+ https://bugs.webkit.org/show_bug.cgi?id=191535 >+ <rdar://problem/47102027> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ This patch: >+ 1) adds support for U2F mocking mechanism; >+ 2) updates tests to reflect U2fCommandConstructor changes. >+ >+ * TestWebKitAPI/Tests/WebCore/CtapResponseTest.cpp: >+ (TestWebKitAPI::TEST): >+ * TestWebKitAPI/Tests/WebCore/FidoTestData.h: >+ * WebKitTestRunner/InjectedBundle/TestRunner.cpp: >+ (WTR::TestRunner::setWebAuthenticationMockConfiguration): >+ > 2019-01-07 Fujii Hironori <Hironori.Fujii@sony.com> > > [Win] EWS: wincairo-ews cannot apply a patch with *.png >diff --git a/Tools/TestWebKitAPI/Tests/WebCore/CtapResponseTest.cpp b/Tools/TestWebKitAPI/Tests/WebCore/CtapResponseTest.cpp >index de3f209293309c4bbc2320086b4aaf960dec4b8a..a7dcab0b76a728f892e7b4390fc485ac8e717f8b 100644 >--- a/Tools/TestWebKitAPI/Tests/WebCore/CtapResponseTest.cpp >+++ b/Tools/TestWebKitAPI/Tests/WebCore/CtapResponseTest.cpp >@@ -520,7 +520,7 @@ TEST(CTAPResponseTest, TestParseIncorrectRegisterResponseData5) > // Tests that U2F authenticator data is properly serialized. > TEST(CTAPResponseTest, TestParseSignResponseData) > { >- auto response = readFromU2fSignResponse(TestData::kRelyingPartyId, getTestCredentialRawIdBytes(), getTestSignResponse()); >+ auto response = readU2fSignResponse(TestData::kRelyingPartyId, getTestCredentialRawIdBytes(), getTestSignResponse()); > ASSERT_TRUE(response); > EXPECT_EQ(response->rawId->byteLength(), sizeof(TestData::kU2fSignKeyHandle)); > EXPECT_EQ(memcmp(response->rawId->data(), TestData::kU2fSignKeyHandle, sizeof(TestData::kU2fSignKeyHandle)), 0); >@@ -533,27 +533,27 @@ TEST(CTAPResponseTest, TestParseSignResponseData) > > TEST(CTAPResponseTest, TestParseU2fSignWithNullKeyHandle) > { >- auto response = readFromU2fSignResponse(TestData::kRelyingPartyId, Vector<uint8_t>(), getTestSignResponse()); >+ auto response = readU2fSignResponse(TestData::kRelyingPartyId, Vector<uint8_t>(), getTestSignResponse()); > EXPECT_FALSE(response); > } > > TEST(CTAPResponseTest, TestParseU2fSignWithNullResponse) > { >- auto response = readFromU2fSignResponse(TestData::kRelyingPartyId, getTestCredentialRawIdBytes(), Vector<uint8_t>()); >+ auto response = readU2fSignResponse(TestData::kRelyingPartyId, getTestCredentialRawIdBytes(), Vector<uint8_t>()); > EXPECT_FALSE(response); > } > > TEST(CTAPResponseTest, TestParseU2fSignWithCorruptedCounter) > { > // A sign response of less than 5 bytes. >- auto response = readFromU2fSignResponse(TestData::kRelyingPartyId, getTestCredentialRawIdBytes(), getTestCorruptedSignResponse(3)); >+ auto response = readU2fSignResponse(TestData::kRelyingPartyId, getTestCredentialRawIdBytes(), getTestCorruptedSignResponse(3)); > EXPECT_FALSE(response); > } > > TEST(CTAPResponseTest, TestParseU2fSignWithCorruptedSignature) > { > // A sign response no more than 5 bytes. >- auto response = readFromU2fSignResponse(TestData::kRelyingPartyId, getTestCredentialRawIdBytes(), getTestCorruptedSignResponse(5)); >+ auto response = readU2fSignResponse(TestData::kRelyingPartyId, getTestCredentialRawIdBytes(), getTestCorruptedSignResponse(5)); > EXPECT_FALSE(response); > } > >diff --git a/Tools/TestWebKitAPI/Tests/WebCore/FidoTestData.h b/Tools/TestWebKitAPI/Tests/WebCore/FidoTestData.h >index 38e03c886b5969acadea8d3e50170bce26d07d8c..b682fb7c287c30d30fdc0b540ba695224e32a402 100644 >--- a/Tools/TestWebKitAPI/Tests/WebCore/FidoTestData.h >+++ b/Tools/TestWebKitAPI/Tests/WebCore/FidoTestData.h >@@ -51,7 +51,7 @@ constexpr char kRelyingPartyId[] = "acme.com"; > > constexpr uint8_t kU2fRegisterCommandApdu[] = { > // CLA, INS, P1, P2 APDU instructions >- 0x00, 0x01, 0x00, 0x00, >+ 0x00, 0x01, 0x03, 0x00, > // Data length in 3 bytes in big endian order. > 0x00, 0x00, 0x40, > // Challenge parameter -- see kClientDataHash >@@ -141,7 +141,7 @@ constexpr uint8_t kU2fCheckOnlySignCommandApdu[] = { > > constexpr uint8_t kU2fFakeRegisterCommand[] = { > // CLA, INS, P1, P2 APDU instructions >- 0x00, 0x01, 0x00, 0x00, >+ 0x00, 0x01, 0x03, 0x00, > // Data length in 3 bytes in big endian order. > 0x00, 0x00, 0x40, > // Bogus challenge parameter >diff --git a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp >index 7c8213edebbbc3a63eed838eee5ff1183a58a3dd..24d340f006ed58c30f5c7138901186658a152c23 100644 >--- a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp >+++ b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp >@@ -2509,10 +2509,36 @@ void TestRunner::setWebAuthenticationMockConfiguration(JSValueRef configurationV > JSRetainPtr<JSStringRef> payloadBase64PropertyName(Adopt, JSStringCreateWithUTF8CString("payloadBase64")); > JSValueRef payloadBase64Value = JSObjectGetProperty(context, hid, payloadBase64PropertyName.get(), 0); > if (!JSValueIsUndefined(context, payloadBase64Value) && !JSValueIsNull(context, payloadBase64Value)) { >- if (!JSValueIsString(context, payloadBase64Value)) >+ if (!JSValueIsArray(context, payloadBase64Value)) > return; >+ >+ JSObjectRef payloadBase64 = JSValueToObject(context, payloadBase64Value, nullptr); >+ static auto lengthProperty = adopt(JSStringCreateWithUTF8CString("length")); >+ JSValueRef payloadBase64LengthValue = JSObjectGetProperty(context, payloadBase64, lengthProperty.get(), nullptr); >+ if (!JSValueIsNumber(context, payloadBase64LengthValue)) >+ return; >+ >+ auto payloadBase64s = adoptWK(WKMutableArrayCreate()); >+ auto payloadBase64Length = static_cast<size_t>(JSValueToNumber(context, payloadBase64LengthValue, nullptr)); >+ for (size_t i = 0; i < payloadBase64Length; ++i) { >+ JSValueRef payloadBase64Value = JSObjectGetPropertyAtIndex(context, payloadBase64, i, nullptr); >+ if (!JSValueIsString(context, payloadBase64Value)) >+ continue; >+ WKArrayAppendItem(payloadBase64s.get(), toWK(adopt(JSValueToStringCopy(context, payloadBase64Value, 0)).get()).get()); >+ } >+ > hidKeys.append({ AdoptWK, WKStringCreateWithUTF8CString("PayloadBase64") }); >- hidValues.append(toWK(adopt(JSValueToStringCopy(context, payloadBase64Value, 0)).get())); >+ hidValues.append(payloadBase64s); >+ } >+ >+ JSRetainPtr<JSStringRef> isU2fPropertyName(Adopt, JSStringCreateWithUTF8CString("isU2f")); >+ JSValueRef isU2fValue = JSObjectGetProperty(context, hid, isU2fPropertyName.get(), 0); >+ if (!JSValueIsUndefined(context, isU2fValue) && !JSValueIsNull(context, isU2fValue)) { >+ if (!JSValueIsBoolean(context, isU2fValue)) >+ return; >+ bool isU2f = JSValueToBoolean(context, isU2fValue); >+ hidKeys.append({ AdoptWK, WKStringCreateWithUTF8CString("IsU2f") }); >+ hidValues.append(adoptWK(WKBooleanCreate(isU2f)).get()); > } > > JSRetainPtr<JSStringRef> keepAlivePropertyName(Adopt, JSStringCreateWithUTF8CString("keepAlive")); >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index 763ea36bc98be0d74c0e89316b200867f5348ec5..f503f50d6f316dae048faec108b6b25d71e4367f 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,36 @@ >+2019-01-07 Jiewen Tan <jiewen_tan@apple.com> >+ >+ [WebAuthN] Support U2F HID Authenticators on macOS >+ https://bugs.webkit.org/show_bug.cgi?id=191535 >+ <rdar://problem/47102027> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Besiding adding tests for U2F authenticators, it also changes payloadBase64 from >+ a string to a vector of strings. >+ >+ * http/wpt/webauthn/ctap-hid-failure.https.html: >+ * http/wpt/webauthn/ctap-hid-success.https.html: >+ * http/wpt/webauthn/public-key-credential-create-failure-hid-silent.https.html: >+ * http/wpt/webauthn/public-key-credential-create-failure-hid.https.html: >+ * http/wpt/webauthn/public-key-credential-create-failure-u2f-silent.https-expected.txt: Added. >+ * http/wpt/webauthn/public-key-credential-create-failure-u2f-silent.https.html: Added. >+ * http/wpt/webauthn/public-key-credential-create-failure-u2f.https-expected.txt: Added. >+ * http/wpt/webauthn/public-key-credential-create-failure-u2f.https.html: Added. >+ * http/wpt/webauthn/public-key-credential-create-success-hid.https.html: >+ * http/wpt/webauthn/public-key-credential-create-success-u2f.https-expected.txt: Added. >+ * http/wpt/webauthn/public-key-credential-create-success-u2f.https.html: Copied from LayoutTests/http/wpt/webauthn/public-key-credential-create-success-hid.https.html. >+ * http/wpt/webauthn/public-key-credential-get-failure-hid-silent.https.html: >+ * http/wpt/webauthn/public-key-credential-get-failure-hid.https.html: >+ * http/wpt/webauthn/public-key-credential-get-failure-u2f-silent.https-expected.txt: Added. >+ * http/wpt/webauthn/public-key-credential-get-failure-u2f-silent.https.html: Added. >+ * http/wpt/webauthn/public-key-credential-get-failure-u2f.https-expected.txt: Added. >+ * http/wpt/webauthn/public-key-credential-get-failure-u2f.https.html: Added. >+ * http/wpt/webauthn/public-key-credential-get-success-hid.https.html: >+ * http/wpt/webauthn/public-key-credential-get-success-u2f.https-expected.txt: Added. >+ * http/wpt/webauthn/public-key-credential-get-success-u2f.https.html: Added. >+ * http/wpt/webauthn/resources/util.js: >+ > 2019-01-07 Youenn Fablet <youenn@apple.com> > > LayoutTests/http/wpt/fetch/csp-reports-bypass-csp-checks.html is flaky >diff --git a/LayoutTests/http/wpt/webauthn/ctap-hid-failure.https.html b/LayoutTests/http/wpt/webauthn/ctap-hid-failure.https.html >index e24405fafe7a9f77bb30742162e0b18884caafd1..c46ee39138f0efecf741296edae7f9328a54ce96 100644 >--- a/LayoutTests/http/wpt/webauthn/ctap-hid-failure.https.html >+++ b/LayoutTests/http/wpt/webauthn/ctap-hid-failure.https.html >@@ -64,7 +64,7 @@ > > promise_test(function(t) { > if (window.testRunner) >- testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "wrong-channel-id", payloadBase64:testDummyMessagePayloadBase64 } }); >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "wrong-channel-id", payloadBase64:[testDummyMessagePayloadBase64] } }); > return promiseRejects(t, "UnknownError", navigator.credentials.create(defaultOptions), "Unknown internal error. Error code: -1"); > }, "CTAP HID with request::msg stage wrong channel id error in a mock hid authenticator."); > </script> >diff --git a/LayoutTests/http/wpt/webauthn/ctap-hid-success.https.html b/LayoutTests/http/wpt/webauthn/ctap-hid-success.https.html >index db02d5418a6c05c384ae5a8226f5bf299b3ae87e..8e5d095e5fd983faaac4d7e3a462b0ccdd97d605 100644 >--- a/LayoutTests/http/wpt/webauthn/ctap-hid-success.https.html >+++ b/LayoutTests/http/wpt/webauthn/ctap-hid-success.https.html >@@ -21,7 +21,7 @@ > > promise_test(function(t) { > if (window.testRunner) >- testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", payloadBase64: testCreationMessageBase64, keepAlive: true } }); >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", payloadBase64: [testCreationMessageBase64], keepAlive: true } }); > return navigator.credentials.create(defaultOptions).then(credential => { > assert_not_equals(credential, undefined); > assert_not_equals(credential, null); >@@ -30,7 +30,7 @@ > > promise_test(function(t) { > if (window.testRunner) >- testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", payloadBase64: testCreationMessageBase64, fastDataArrival: true } }); >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", payloadBase64: [testCreationMessageBase64], fastDataArrival: true } }); > return navigator.credentials.create(defaultOptions).then(credential => { > assert_not_equals(credential, undefined); > assert_not_equals(credential, null); >@@ -39,7 +39,7 @@ > > promise_test(function(t) { > if (window.testRunner) >- testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "info", subStage: "init", error: "empty-report", payloadBase64: testCreationMessageBase64, continueAfterErrorData: true } }); >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "info", subStage: "init", error: "empty-report", payloadBase64: [testCreationMessageBase64], continueAfterErrorData: true } }); > return navigator.credentials.create(defaultOptions).then(credential => { > assert_not_equals(credential, undefined); > assert_not_equals(credential, null); >@@ -48,7 +48,7 @@ > > promise_test(function(t) { > if (window.testRunner) >- testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "info", subStage: "init", error: "wrong-channel-id", payloadBase64: testCreationMessageBase64, continueAfterErrorData: true } }); >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "info", subStage: "init", error: "wrong-channel-id", payloadBase64: [testCreationMessageBase64], continueAfterErrorData: true } }); > return navigator.credentials.create(defaultOptions).then(credential => { > assert_not_equals(credential, undefined); > assert_not_equals(credential, null); >@@ -57,7 +57,7 @@ > > promise_test(function(t) { > if (window.testRunner) >- testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "info", subStage: "init", error: "wrong-nonce", payloadBase64: testCreationMessageBase64, continueAfterErrorData: true } }); >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "info", subStage: "init", error: "wrong-nonce", payloadBase64: [testCreationMessageBase64], continueAfterErrorData: true } }); > return navigator.credentials.create(defaultOptions).then(credential => { > assert_not_equals(credential, undefined); > assert_not_equals(credential, null); >diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-hid-silent.https.html b/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-hid-silent.https.html >index b9953f9f5b3a556f1b52bab52b4b9c0bc987de38..66004a32153e6544cb0886ff84a6f91edd646565 100644 >--- a/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-hid-silent.https.html >+++ b/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-hid-silent.https.html >@@ -22,7 +22,7 @@ > }; > > if (window.testRunner) >- testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, hid: { stage: "request", subStage: "msg", error: "malicious-payload", payloadBase64: testDummyMessagePayloadBase64 } }); >+ testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, hid: { stage: "request", subStage: "msg", error: "malicious-payload", payloadBase64: [testDummyMessagePayloadBase64] } }); > return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out."); > }, "PublicKeyCredential's [[create]] with malicious payload in a mock hid authenticator."); > >diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-hid.https.html b/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-hid.https.html >index 2dde6d09aa12b90bba70a4ff1a4f914aee7c9d50..f163a396f2add938928bffd3761ef57f4afcdd39 100644 >--- a/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-hid.https.html >+++ b/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-hid.https.html >@@ -46,7 +46,7 @@ > }; > > if (window.testRunner) >- testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "malicious-payload", payloadBase64: testDummyMessagePayloadBase64 } }); >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "malicious-payload", payloadBase64: [testDummyMessagePayloadBase64] } }); > return promiseRejects(t, "UnknownError", navigator.credentials.create(options), "Unknown internal error. Error code: -1"); > }, "PublicKeyCredential's [[create]] with malicious payload in a mock hid authenticator."); > >diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-u2f-silent.https-expected.txt b/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-u2f-silent.https-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..1063b7a2825fba77264357f14c33fa484d6615dc >--- /dev/null >+++ b/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-u2f-silent.https-expected.txt >@@ -0,0 +1,9 @@ >+ >+PASS PublicKeyCredential's [[create]] with malformed APDU payload in a mock hid authenticator. >+PASS PublicKeyCredential's [[create]] with malformed U2F register response in a mock hid authenticator. >+PASS PublicKeyCredential's [[create]] with register command error in a mock hid authenticator. >+PASS PublicKeyCredential's [[create]] with bogus command error in a mock hid authenticator. >+PASS PublicKeyCredential's [[create]] with first exclude credential matched in a mock hid authenticator. >+PASS PublicKeyCredential's [[create]] with second exclude credential matched in a mock hid authenticator. >+PASS PublicKeyCredential's [[create]] with first exclude credential matched in a mock hid authenticator. Test of user presence. >+ >diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-u2f-silent.https.html b/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-u2f-silent.https.html >new file mode 100644 >index 0000000000000000000000000000000000000000..5417998bc718714a98d30a5487225f10bcb26f60 >--- /dev/null >+++ b/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-u2f-silent.https.html >@@ -0,0 +1,165 @@ >+<!DOCTYPE html> >+<title>Web Authentication API: PublicKeyCredential's [[create]] failure cases with a mock u2f authenticator.</title> >+<script src="/resources/testharness.js"></script> >+<script src="/resources/testharnessreport.js"></script> >+<script src="./resources/util.js"></script> >+<script> >+ promise_test(function(t) { >+ const options = { >+ publicKey: { >+ rp: { >+ name: "example.com" >+ }, >+ user: { >+ name: "John Appleseed", >+ id: asciiToUint8Array("123456"), >+ displayName: "John", >+ }, >+ challenge: asciiToUint8Array("123456"), >+ pubKeyCredParams: [{ type: "public-key", alg: -7 }], >+ timeout: 10 >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, hid: { stage: "request", subStage: "msg", error: "malicious-payload", isU2f: true, payloadBase64: ["AQ=="] } }); >+ return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out."); >+ }, "PublicKeyCredential's [[create]] with malformed APDU payload in a mock hid authenticator."); >+ >+ promise_test(function(t) { >+ const options = { >+ publicKey: { >+ rp: { >+ name: "example.com" >+ }, >+ user: { >+ name: "John Appleseed", >+ id: asciiToUint8Array("123456"), >+ displayName: "John", >+ }, >+ challenge: asciiToUint8Array("123456"), >+ pubKeyCredParams: [{ type: "public-key", alg: -7 }], >+ timeout: 10 >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, hid: { stage: "request", subStage: "msg", error: "malicious-payload", isU2f: true, payloadBase64: [testU2fApduNoErrorOnlyResponseBase64] } }); >+ return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out."); >+ }, "PublicKeyCredential's [[create]] with malformed U2F register response in a mock hid authenticator."); >+ >+ promise_test(function(t) { >+ const options = { >+ publicKey: { >+ rp: { >+ name: "example.com" >+ }, >+ user: { >+ name: "John Appleseed", >+ id: asciiToUint8Array("123456"), >+ displayName: "John", >+ }, >+ challenge: asciiToUint8Array("123456"), >+ pubKeyCredParams: [{ type: "public-key", alg: -7 }], >+ timeout: 10 >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, hid: { stage: "request", subStage: "msg", error: "malicious-payload", isU2f: true, payloadBase64: [testU2fApduInsNotSupportedOnlyResponseBase64] } }); >+ return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out."); >+ }, "PublicKeyCredential's [[create]] with register command error in a mock hid authenticator."); >+ >+ promise_test(function(t) { >+ const options = { >+ publicKey: { >+ rp: { >+ name: "example.com" >+ }, >+ user: { >+ name: "John Appleseed", >+ id: asciiToUint8Array("123456"), >+ displayName: "John", >+ }, >+ challenge: asciiToUint8Array("123456"), >+ pubKeyCredParams: [{ type: "public-key", alg: -7 }], >+ excludeCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }], >+ timeout: 10 >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, hid: { stage: "request", subStage: "msg", error: "malicious-payload", isU2f: true, payloadBase64: [testU2fApduNoErrorOnlyResponseBase64, testU2fApduInsNotSupportedOnlyResponseBase64] } }); >+ return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out."); >+ }, "PublicKeyCredential's [[create]] with bogus command error in a mock hid authenticator."); >+ >+ promise_test(function(t) { >+ const options = { >+ publicKey: { >+ rp: { >+ name: "example.com" >+ }, >+ user: { >+ name: "John Appleseed", >+ id: asciiToUint8Array("123456"), >+ displayName: "John", >+ }, >+ challenge: asciiToUint8Array("123456"), >+ pubKeyCredParams: [{ type: "public-key", alg: -7 }], >+ excludeCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }], >+ timeout: 10 >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, hid: { stage: "request", subStage: "msg", error: "malicious-payload", isU2f: true, payloadBase64: [testU2fApduNoErrorOnlyResponseBase64, testU2fApduNoErrorOnlyResponseBase64] } }); >+ return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out."); >+ }, "PublicKeyCredential's [[create]] with first exclude credential matched in a mock hid authenticator."); >+ >+ // Match the second exclude credential. >+ promise_test(function(t) { >+ const options = { >+ publicKey: { >+ rp: { >+ name: "example.com" >+ }, >+ user: { >+ name: "John Appleseed", >+ id: asciiToUint8Array("123456"), >+ displayName: "John", >+ }, >+ challenge: asciiToUint8Array("123456"), >+ pubKeyCredParams: [{ type: "public-key", alg: -7 }], >+ excludeCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }, { type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }], // The content doesn't matter. >+ timeout: 10 >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, hid: { stage: "request", subStage: "msg", error: "malicious-payload", isU2f: true, payloadBase64: [testU2fApduWrongDataOnlyResponseBase64, testU2fApduNoErrorOnlyResponseBase64, testU2fApduNoErrorOnlyResponseBase64] } }); >+ return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out."); >+ }, "PublicKeyCredential's [[create]] with second exclude credential matched in a mock hid authenticator."); >+ >+ promise_test(function(t) { >+ const options = { >+ publicKey: { >+ rp: { >+ name: "example.com" >+ }, >+ user: { >+ name: "John Appleseed", >+ id: asciiToUint8Array("123456"), >+ displayName: "John", >+ }, >+ challenge: asciiToUint8Array("123456"), >+ pubKeyCredParams: [{ type: "public-key", alg: -7 }], >+ excludeCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }], >+ timeout: 10 >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, hid: { stage: "request", subStage: "msg", error: "malicious-payload", isU2f: true, payloadBase64: [testU2fApduConditionsNotSatisfiedOnlyResponseBase64, testU2fApduConditionsNotSatisfiedOnlyResponseBase64, testU2fApduConditionsNotSatisfiedOnlyResponseBase64, testU2fApduNoErrorOnlyResponseBase64] } }); >+ return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out."); >+ }, "PublicKeyCredential's [[create]] with first exclude credential matched in a mock hid authenticator. Test of user presence."); >+</script> >diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-u2f.https-expected.txt b/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-u2f.https-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..1063b7a2825fba77264357f14c33fa484d6615dc >--- /dev/null >+++ b/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-u2f.https-expected.txt >@@ -0,0 +1,9 @@ >+ >+PASS PublicKeyCredential's [[create]] with malformed APDU payload in a mock hid authenticator. >+PASS PublicKeyCredential's [[create]] with malformed U2F register response in a mock hid authenticator. >+PASS PublicKeyCredential's [[create]] with register command error in a mock hid authenticator. >+PASS PublicKeyCredential's [[create]] with bogus command error in a mock hid authenticator. >+PASS PublicKeyCredential's [[create]] with first exclude credential matched in a mock hid authenticator. >+PASS PublicKeyCredential's [[create]] with second exclude credential matched in a mock hid authenticator. >+PASS PublicKeyCredential's [[create]] with first exclude credential matched in a mock hid authenticator. Test of user presence. >+ >diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-u2f.https.html b/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-u2f.https.html >new file mode 100644 >index 0000000000000000000000000000000000000000..157d9e88a06bed46c7d4fd85e1601ce2aecf423d >--- /dev/null >+++ b/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-u2f.https.html >@@ -0,0 +1,158 @@ >+<!DOCTYPE html> >+<title>Web Authentication API: PublicKeyCredential's [[create]] failure cases with a mock u2f authenticator.</title> >+<script src="/resources/testharness.js"></script> >+<script src="/resources/testharnessreport.js"></script> >+<script src="./resources/util.js"></script> >+<script> >+ promise_test(function(t) { >+ const options = { >+ publicKey: { >+ rp: { >+ name: "example.com" >+ }, >+ user: { >+ name: "John Appleseed", >+ id: asciiToUint8Array("123456"), >+ displayName: "John", >+ }, >+ challenge: asciiToUint8Array("123456"), >+ pubKeyCredParams: [{ type: "public-key", alg: -7 }] >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "malicious-payload", isU2f: true, payloadBase64: ["AQ=="] } }); >+ return promiseRejects(t, "UnknownError", navigator.credentials.create(options), "Couldn't parse the APDU response."); >+ }, "PublicKeyCredential's [[create]] with malformed APDU payload in a mock hid authenticator."); >+ >+ promise_test(function(t) { >+ const options = { >+ publicKey: { >+ rp: { >+ name: "example.com" >+ }, >+ user: { >+ name: "John Appleseed", >+ id: asciiToUint8Array("123456"), >+ displayName: "John", >+ }, >+ challenge: asciiToUint8Array("123456"), >+ pubKeyCredParams: [{ type: "public-key", alg: -7 }] >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "malicious-payload", isU2f: true, payloadBase64: [testU2fApduNoErrorOnlyResponseBase64] } }); >+ return promiseRejects(t, "UnknownError", navigator.credentials.create(options), "Couldn't parse the U2F register response."); >+ }, "PublicKeyCredential's [[create]] with malformed U2F register response in a mock hid authenticator."); >+ >+ promise_test(function(t) { >+ const options = { >+ publicKey: { >+ rp: { >+ name: "example.com" >+ }, >+ user: { >+ name: "John Appleseed", >+ id: asciiToUint8Array("123456"), >+ displayName: "John", >+ }, >+ challenge: asciiToUint8Array("123456"), >+ pubKeyCredParams: [{ type: "public-key", alg: -7 }] >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "malicious-payload", isU2f: true, payloadBase64: [testU2fApduInsNotSupportedOnlyResponseBase64] } }); >+ return promiseRejects(t, "UnknownError", navigator.credentials.create(options), "Unknown internal error. Error code: 27904"); >+ }, "PublicKeyCredential's [[create]] with register command error in a mock hid authenticator."); >+ >+ promise_test(function(t) { >+ const options = { >+ publicKey: { >+ rp: { >+ name: "example.com" >+ }, >+ user: { >+ name: "John Appleseed", >+ id: asciiToUint8Array("123456"), >+ displayName: "John", >+ }, >+ challenge: asciiToUint8Array("123456"), >+ pubKeyCredParams: [{ type: "public-key", alg: -7 }], >+ excludeCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }] >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "malicious-payload", isU2f: true, payloadBase64: [testU2fApduNoErrorOnlyResponseBase64, testU2fApduInsNotSupportedOnlyResponseBase64] } }); >+ return promiseRejects(t, "UnknownError", navigator.credentials.create(options), "Unknown internal error. Error code: 27904"); >+ }, "PublicKeyCredential's [[create]] with bogus command error in a mock hid authenticator."); >+ >+ promise_test(function(t) { >+ const options = { >+ publicKey: { >+ rp: { >+ name: "example.com" >+ }, >+ user: { >+ name: "John Appleseed", >+ id: asciiToUint8Array("123456"), >+ displayName: "John", >+ }, >+ challenge: asciiToUint8Array("123456"), >+ pubKeyCredParams: [{ type: "public-key", alg: -7 }], >+ excludeCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }] >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "malicious-payload", isU2f: true, payloadBase64: [testU2fApduNoErrorOnlyResponseBase64, testU2fApduNoErrorOnlyResponseBase64] } }); >+ return promiseRejects(t, "InvalidStateError", navigator.credentials.create(options), "At least one credential matches an entry of the excludeCredentials list in the authenticator."); >+ }, "PublicKeyCredential's [[create]] with first exclude credential matched in a mock hid authenticator."); >+ >+ // Match the second exclude credential. >+ promise_test(function(t) { >+ const options = { >+ publicKey: { >+ rp: { >+ name: "example.com" >+ }, >+ user: { >+ name: "John Appleseed", >+ id: asciiToUint8Array("123456"), >+ displayName: "John", >+ }, >+ challenge: asciiToUint8Array("123456"), >+ pubKeyCredParams: [{ type: "public-key", alg: -7 }], >+ excludeCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }, { type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }] // The content doesn't matter. >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "malicious-payload", isU2f: true, payloadBase64: [testU2fApduWrongDataOnlyResponseBase64, testU2fApduNoErrorOnlyResponseBase64, testU2fApduNoErrorOnlyResponseBase64] } }); >+ return promiseRejects(t, "InvalidStateError", navigator.credentials.create(options), "At least one credential matches an entry of the excludeCredentials list in the authenticator."); >+ }, "PublicKeyCredential's [[create]] with second exclude credential matched in a mock hid authenticator."); >+ >+ promise_test(function(t) { >+ const options = { >+ publicKey: { >+ rp: { >+ name: "example.com" >+ }, >+ user: { >+ name: "John Appleseed", >+ id: asciiToUint8Array("123456"), >+ displayName: "John", >+ }, >+ challenge: asciiToUint8Array("123456"), >+ pubKeyCredParams: [{ type: "public-key", alg: -7 }], >+ excludeCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }] >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "malicious-payload", isU2f: true, payloadBase64: [testU2fApduConditionsNotSatisfiedOnlyResponseBase64, testU2fApduConditionsNotSatisfiedOnlyResponseBase64, testU2fApduConditionsNotSatisfiedOnlyResponseBase64, testU2fApduNoErrorOnlyResponseBase64] } }); >+ return promiseRejects(t, "InvalidStateError", navigator.credentials.create(options), "At least one credential matches an entry of the excludeCredentials list in the authenticator."); >+ }, "PublicKeyCredential's [[create]] with first exclude credential matched in a mock hid authenticator. Test of user presence."); >+</script> >diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-create-success-hid.https.html b/LayoutTests/http/wpt/webauthn/public-key-credential-create-success-hid.https.html >index 5d93b807a44c53734a7f5d811df6013562c05c7c..0d79a9ad9a6b98474c75bfc13d58c1de0741aa67 100644 >--- a/LayoutTests/http/wpt/webauthn/public-key-credential-create-success-hid.https.html >+++ b/LayoutTests/http/wpt/webauthn/public-key-credential-create-success-hid.https.html >@@ -7,7 +7,7 @@ > <script> > // Default mock configuration. Tests need to override if they need different configuration. > if (window.testRunner) >- testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", payloadBase64: testCreationMessageBase64 } }); >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", payloadBase64: [testCreationMessageBase64] } }); > > function checkResult(credential) > { >diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-create-success-u2f.https-expected.txt b/LayoutTests/http/wpt/webauthn/public-key-credential-create-success-u2f.https-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..57b2617fa887f7131c2c0d8d239a8979bb3c4e5f >--- /dev/null >+++ b/LayoutTests/http/wpt/webauthn/public-key-credential-create-success-u2f.https-expected.txt >@@ -0,0 +1,6 @@ >+ >+PASS PublicKeyCredential's [[create]] with minimum options in a mock u2f authenticator. >+PASS PublicKeyCredential's [[create]] with excludeCredentials in a mock u2f authenticator. >+PASS PublicKeyCredential's [[create]] with excludeCredentials in a mock u2f authenticator. 2 >+PASS PublicKeyCredential's [[create]] with test of user presence in a mock u2f authenticator. >+ >diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-create-success-u2f.https.html b/LayoutTests/http/wpt/webauthn/public-key-credential-create-success-u2f.https.html >new file mode 100644 >index 0000000000000000000000000000000000000000..aab18d17db58869816683e85fb033c9808c849b1 >--- /dev/null >+++ b/LayoutTests/http/wpt/webauthn/public-key-credential-create-success-u2f.https.html >@@ -0,0 +1,129 @@ >+<!DOCTYPE html> >+<title>Web Authentication API: PublicKeyCredential's [[create]] success cases with a mock u2f authenticator.</title> >+<script src="/resources/testharness.js"></script> >+<script src="/resources/testharnessreport.js"></script> >+<script src="./resources/util.js"></script> >+<script src="./resources/cbor.js"></script> >+<script> >+ function checkResult(credential) >+ { >+ // Check response >+ assert_array_equals(Base64URL.parse(credential.id), Base64URL.parse(testU2fCredentialIdBase64)); >+ assert_equals(credential.type, 'public-key'); >+ assert_array_equals(new Uint8Array(credential.rawId), Base64URL.parse(testU2fCredentialIdBase64)); >+ assert_equals(bytesToASCIIString(credential.response.clientDataJSON), '{"type":"webauthn.create","challenge":"MTIzNDU2","origin":"https://localhost:9443"}'); >+ assert_throws("NotSupportedError", () => { credential.getClientExtensionResults() }); >+ >+ // Check attestation >+ const attestationObject = CBOR.decode(credential.response.attestationObject); >+ assert_equals(attestationObject.fmt, "fido-u2f"); >+ // Check authData >+ const authData = decodeAuthData(attestationObject.authData); >+ assert_equals(bytesToHexString(authData.rpIdHash), "49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763"); >+ assert_equals(authData.flags, 65); >+ assert_equals(authData.counter, 0); >+ assert_equals(bytesToHexString(authData.aaguid), "00000000000000000000000000000000"); >+ assert_array_equals(authData.credentialID, Base64URL.parse(testU2fCredentialIdBase64)); >+ // Check fido-u2f attestation >+ assert_true(checkPublicKey(authData.publicKey)); >+ assert_equals(attestationObject.attStmt.x5c.length, 1); >+ } >+ >+ promise_test(t => { >+ const options = { >+ publicKey: { >+ rp: { >+ name: "localhost", >+ }, >+ user: { >+ name: "John Appleseed", >+ id: Base64URL.parse(testUserhandleBase64), >+ displayName: "Appleseed", >+ }, >+ challenge: Base64URL.parse("MTIzNDU2"), >+ pubKeyCredParams: [{ type: "public-key", alg: -7 }], >+ timeout: 10 >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", isU2f: true, payloadBase64: [testU2fRegisterResponse] } }); >+ return navigator.credentials.create(options).then(credential => { >+ checkResult(credential); >+ }); >+ }, "PublicKeyCredential's [[create]] with minimum options in a mock u2f authenticator."); >+ >+ promise_test(t => { >+ const options = { >+ publicKey: { >+ rp: { >+ name: "localhost", >+ }, >+ user: { >+ name: "John Appleseed", >+ id: Base64URL.parse(testUserhandleBase64), >+ displayName: "Appleseed", >+ }, >+ challenge: Base64URL.parse("MTIzNDU2"), >+ pubKeyCredParams: [{ type: "public-key", alg: -7 }], >+ excludeCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }], >+ timeout: 10 >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", isU2f: true, payloadBase64: [testU2fApduWrongDataOnlyResponseBase64, testU2fRegisterResponse] } }); >+ return navigator.credentials.create(options).then(credential => { >+ checkResult(credential); >+ }); >+ }, "PublicKeyCredential's [[create]] with excludeCredentials in a mock u2f authenticator."); >+ >+ promise_test(t => { >+ const options = { >+ publicKey: { >+ rp: { >+ name: "localhost", >+ }, >+ user: { >+ name: "John Appleseed", >+ id: Base64URL.parse(testUserhandleBase64), >+ displayName: "Appleseed", >+ }, >+ challenge: Base64URL.parse("MTIzNDU2"), >+ pubKeyCredParams: [{ type: "public-key", alg: -7 }], >+ excludeCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }, { type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }], // The content doesn't matter. >+ timeout: 10 >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", isU2f: true, payloadBase64: [testU2fApduWrongDataOnlyResponseBase64, testU2fApduWrongDataOnlyResponseBase64, testU2fRegisterResponse] } }); >+ return navigator.credentials.create(options).then(credential => { >+ checkResult(credential); >+ }); >+ }, "PublicKeyCredential's [[create]] with excludeCredentials in a mock u2f authenticator. 2"); >+ >+ promise_test(t => { >+ const options = { >+ publicKey: { >+ rp: { >+ name: "localhost", >+ }, >+ user: { >+ name: "John Appleseed", >+ id: Base64URL.parse(testUserhandleBase64), >+ displayName: "Appleseed", >+ }, >+ challenge: Base64URL.parse("MTIzNDU2"), >+ pubKeyCredParams: [{ type: "public-key", alg: -7 }], >+ timeout: 500 >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", isU2f: true, payloadBase64: [testU2fApduConditionsNotSatisfiedOnlyResponseBase64, testU2fApduConditionsNotSatisfiedOnlyResponseBase64, testU2fRegisterResponse] } }); >+ return navigator.credentials.create(options).then(credential => { >+ checkResult(credential); >+ }); >+ }, "PublicKeyCredential's [[create]] with test of user presence in a mock u2f authenticator."); >+</script> >diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid-silent.https.html b/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid-silent.https.html >index e3636c3e75b7d60161d500b212ef15884607c025..144ba9d89a9bd8606d944780d44bba1f95202321 100644 >--- a/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid-silent.https.html >+++ b/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid-silent.https.html >@@ -13,7 +13,7 @@ > }; > > if (window.testRunner) >- testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, hid: { stage: "request", subStage: "msg", error: "malicious-payload", payloadBase64: testDummyMessagePayloadBase64 } }); >+ testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, hid: { stage: "request", subStage: "msg", error: "malicious-payload", payloadBase64: [testDummyMessagePayloadBase64] } }); > return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Operation timed out."); > }, "PublicKeyCredential's [[get]] with malicious payload in a mock hid authenticator."); > >diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid.https.html b/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid.https.html >index ec6b0a88fa9a4278483b400611c7f99d2885ff7b..5ac21390a252a63aa3759345bd512039f8735f94 100644 >--- a/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid.https.html >+++ b/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-hid.https.html >@@ -32,7 +32,7 @@ > }; > > if (window.testRunner) >- testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "malicious-payload", payloadBase64: testDummyMessagePayloadBase64 } }); >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "malicious-payload", payloadBase64: [testDummyMessagePayloadBase64] } }); > return promiseRejects(t, "UnknownError", navigator.credentials.get(options), "Unknown internal error. Error code: -1"); > }, "PublicKeyCredential's [[get]] with malicious payload in a mock hid authenticator."); > >diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-u2f-silent.https-expected.txt b/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-u2f-silent.https-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..5a06145bd55fac5d469b7e38ae905a9018fbee4d >--- /dev/null >+++ b/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-u2f-silent.https-expected.txt >@@ -0,0 +1,5 @@ >+ >+PASS PublicKeyCredential's [[get]] with malformed sign response in a mock hid authenticator. >+PASS PublicKeyCredential's [[get]] with no matched allow credentials in a mock hid authenticator. >+PASS PublicKeyCredential's [[get]] with no matched allow credentials in a mock hid authenticator. 2 >+ >diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-u2f-silent.https.html b/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-u2f-silent.https.html >new file mode 100644 >index 0000000000000000000000000000000000000000..8a7cd8a5bb91d81b1476add7c42d01363b0d6c7a >--- /dev/null >+++ b/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-u2f-silent.https.html >@@ -0,0 +1,48 @@ >+<!DOCTYPE html> >+<title>Web Authentication API: PublicKeyCredential's [[get]] failure cases with a mock u2f authenticator.</title> >+<script src="/resources/testharness.js"></script> >+<script src="/resources/testharnessreport.js"></script> >+<script src="./resources/util.js"></script> >+<script> >+ promise_test(function(t) { >+ const options = { >+ publicKey: { >+ challenge: asciiToUint8Array("123456"), >+ allowCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }], >+ timeout: 10 >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, hid: { stage: "request", subStage: "msg", error: "malicious-payload", isU2f: true, payloadBase64: [testU2fApduNoErrorOnlyResponseBase64] } }); >+ return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Operation timed out."); >+ }, "PublicKeyCredential's [[get]] with malformed sign response in a mock hid authenticator."); >+ >+ promise_test(function(t) { >+ const options = { >+ publicKey: { >+ challenge: asciiToUint8Array("123456"), >+ allowCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }], >+ timeout: 10 >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, hid: { stage: "request", subStage: "msg", error: "malicious-payload", isU2f: true, payloadBase64: [testU2fApduWrongDataOnlyResponseBase64] } }); >+ return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Operation timed out."); >+ }, "PublicKeyCredential's [[get]] with no matched allow credentials in a mock hid authenticator."); >+ >+ promise_test(function(t) { >+ const options = { >+ publicKey: { >+ challenge: asciiToUint8Array("123456"), >+ allowCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }, { type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }], >+ timeout: 10 >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, hid: { stage: "request", subStage: "msg", error: "malicious-payload", isU2f: true, payloadBase64: [testU2fApduWrongDataOnlyResponseBase64, testU2fApduWrongDataOnlyResponseBase64] } }); >+ return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Operation timed out."); >+ }, "PublicKeyCredential's [[get]] with no matched allow credentials in a mock hid authenticator. 2"); >+</script> >diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-u2f.https-expected.txt b/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-u2f.https-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..5a06145bd55fac5d469b7e38ae905a9018fbee4d >--- /dev/null >+++ b/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-u2f.https-expected.txt >@@ -0,0 +1,5 @@ >+ >+PASS PublicKeyCredential's [[get]] with malformed sign response in a mock hid authenticator. >+PASS PublicKeyCredential's [[get]] with no matched allow credentials in a mock hid authenticator. >+PASS PublicKeyCredential's [[get]] with no matched allow credentials in a mock hid authenticator. 2 >+ >diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-u2f.https.html b/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-u2f.https.html >new file mode 100644 >index 0000000000000000000000000000000000000000..b435b5f5c7f01e66d218315ce009a0ca6d1bf2fc >--- /dev/null >+++ b/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-u2f.https.html >@@ -0,0 +1,45 @@ >+<!DOCTYPE html> >+<title>Web Authentication API: PublicKeyCredential's [[get]] failure cases with a mock u2f authenticator.</title> >+<script src="/resources/testharness.js"></script> >+<script src="/resources/testharnessreport.js"></script> >+<script src="./resources/util.js"></script> >+<script> >+ promise_test(function(t) { >+ const options = { >+ publicKey: { >+ challenge: asciiToUint8Array("123456"), >+ allowCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }] >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "malicious-payload", isU2f: true, payloadBase64: [testU2fApduNoErrorOnlyResponseBase64] } }); >+ return promiseRejects(t, "UnknownError", navigator.credentials.get(options), "Couldn't parse the U2F sign response."); >+ }, "PublicKeyCredential's [[get]] with malformed sign response in a mock hid authenticator."); >+ >+ promise_test(function(t) { >+ const options = { >+ publicKey: { >+ challenge: asciiToUint8Array("123456"), >+ allowCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }] >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "malicious-payload", isU2f: true, payloadBase64: [testU2fApduWrongDataOnlyResponseBase64] } }); >+ return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "No credentials from the allowCredentials list is found in the authenticator."); >+ }, "PublicKeyCredential's [[get]] with no matched allow credentials in a mock hid authenticator."); >+ >+ promise_test(function(t) { >+ const options = { >+ publicKey: { >+ challenge: asciiToUint8Array("123456"), >+ allowCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }, { type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }] >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "malicious-payload", isU2f: true, payloadBase64: [testU2fApduWrongDataOnlyResponseBase64, testU2fApduWrongDataOnlyResponseBase64] } }); >+ return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "No credentials from the allowCredentials list is found in the authenticator."); >+ }, "PublicKeyCredential's [[get]] with no matched allow credentials in a mock hid authenticator. 2"); >+</script> >diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-hid.https.html b/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-hid.https.html >index 67db831007f7a2c5045d72dae3e1cd8001cc24d0..74362466e3fb5381711b92974447a567fc193fe9 100644 >--- a/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-hid.https.html >+++ b/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-hid.https.html >@@ -6,7 +6,7 @@ > <script> > // Default mock configuration. Tests need to override if they need different configuration. > if (window.testRunner) >- testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", payloadBase64: testAssertionMessageBase64 } }); >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", payloadBase64: [testAssertionMessageBase64] } }); > > function checkResult(credential) > { >diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-u2f.https-expected.txt b/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-u2f.https-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..4c4a39eaa28d5a44cc4ac066979243964a9b423c >--- /dev/null >+++ b/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-u2f.https-expected.txt >@@ -0,0 +1,5 @@ >+ >+PASS PublicKeyCredential's [[get]] with minimum options in a mock hid authenticator. >+PASS PublicKeyCredential's [[get]] with more allow credentials in a mock hid authenticator. >+PASS PublicKeyCredential's [[get]] with test of user presence in a mock hid authenticator. >+ >diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-u2f.https.html b/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-u2f.https.html >new file mode 100644 >index 0000000000000000000000000000000000000000..8a6aafccd013c22adcd43e0a358570e1089320f2 >--- /dev/null >+++ b/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-u2f.https.html >@@ -0,0 +1,70 @@ >+<!DOCTYPE html> >+<title>Web Authentication API: PublicKeyCredential's [[get]] success cases with a mock u2f authenticator.</title> >+<script src="/resources/testharness.js"></script> >+<script src="/resources/testharnessreport.js"></script> >+<script src="./resources/util.js"></script> >+<script> >+ function checkResult(credential) >+ { >+ // Check respond >+ assert_array_equals(Base64URL.parse(credential.id), Base64URL.parse(testU2fCredentialIdBase64)); >+ assert_equals(credential.type, 'public-key'); >+ assert_array_equals(new Uint8Array(credential.rawId), Base64URL.parse(testU2fCredentialIdBase64)); >+ assert_equals(bytesToASCIIString(credential.response.clientDataJSON), '{"type":"webauthn.get","challenge":"MTIzNDU2","origin":"https://localhost:9443"}'); >+ assert_equals(credential.response.userHandle, null); >+ >+ // Check authData >+ const authData = decodeAuthData(new Uint8Array(credential.response.authenticatorData)); >+ assert_equals(bytesToHexString(authData.rpIdHash), "49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763"); >+ assert_equals(authData.flags, 1); >+ assert_equals(authData.counter, 59); >+ } >+ >+ promise_test(t => { >+ const options = { >+ publicKey: { >+ challenge: Base64URL.parse("MTIzNDU2"), >+ allowCredentials: [{ type: "public-key", id: Base64URL.parse(testU2fCredentialIdBase64) }], >+ timeout: 10 >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", isU2f: true, payloadBase64: [testU2fSignResponse] } }); >+ return navigator.credentials.get(options).then(credential => { >+ return checkResult(credential); >+ }); >+ }, "PublicKeyCredential's [[get]] with minimum options in a mock hid authenticator."); >+ >+ promise_test(t => { >+ const options = { >+ publicKey: { >+ challenge: Base64URL.parse("MTIzNDU2"), >+ allowCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }, { type: "public-key", id: Base64URL.parse(testU2fCredentialIdBase64) }], >+ timeout: 10 >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", isU2f: true, payloadBase64: [testU2fApduWrongDataOnlyResponseBase64, testU2fSignResponse] } }); >+ return navigator.credentials.get(options).then(credential => { >+ return checkResult(credential); >+ }); >+ }, "PublicKeyCredential's [[get]] with more allow credentials in a mock hid authenticator."); >+ >+ promise_test(t => { >+ const options = { >+ publicKey: { >+ challenge: Base64URL.parse("MTIzNDU2"), >+ allowCredentials: [{ type: "public-key", id: Base64URL.parse(testU2fCredentialIdBase64) }], >+ timeout: 500 >+ } >+ }; >+ >+ if (window.testRunner) >+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", isU2f: true, payloadBase64: [testU2fApduConditionsNotSatisfiedOnlyResponseBase64, testU2fApduConditionsNotSatisfiedOnlyResponseBase64, testU2fSignResponse] } }); >+ return navigator.credentials.get(options).then(credential => { >+ return checkResult(credential); >+ }); >+ }, "PublicKeyCredential's [[get]] with test of user presence in a mock hid authenticator."); >+</script> >diff --git a/LayoutTests/http/wpt/webauthn/resources/util.js b/LayoutTests/http/wpt/webauthn/resources/util.js >index 83f4b48adcf25b7004e9701f47f013f8a059afab..f60986b47227eced31a18f22c0c91f94ac4fa1bd 100644 >--- a/LayoutTests/http/wpt/webauthn/resources/util.js >+++ b/LayoutTests/http/wpt/webauthn/resources/util.js >@@ -69,6 +69,34 @@ const testAssertionMessageBase64 = > "Z51VstuQkuHI2eXh0Ct1gPC0gSx3CWLh5I9a2AEAAABQA1hHMEUCIQCSFTuuBWgB" + > "4/F0VB7DlUVM09IHPmxe1MzHUwRoCRZbCAIgGKov6xoAx2MEf6/6qNs8OutzhP2C" + > "QoJ1L7Fe64G9uBc="; >+const testU2fApduNoErrorOnlyResponseBase64 = "kAA="; >+const testU2fApduInsNotSupportedOnlyResponseBase64 = "bQA="; >+const testU2fApduWrongDataOnlyResponseBase64 = "aoA="; >+const testU2fApduConditionsNotSatisfiedOnlyResponseBase64 = "aYU="; >+const testU2fRegisterResponse = >+ "BQTodiWJbuTkbcAydm6Ah5YvNt+d/otWfzdjAVsZkKYOFCfeYS1mQYvaGVBYHrxc" + >+ "jB2tcQyxTCL4yXBF9GEvsgyRQD69ib937FCXVe6cJjXvqqx7K5xc7xc2w3F9pIU0" + >+ "yMa2VNf/lF9QtcxOeAVb3TlrZPeNosX5YgDM1BXNCP5CADgwggJKMIIBMqADAgEC" + >+ "AgQEbIgiMA0GCSqGSIb3DQEBCwUAMC4xLDAqBgNVBAMTI1l1YmljbyBVMkYgUm9v" + >+ "dCBDQSBTZXJpYWwgNDU3MjAwNjMxMCAXDTE0MDgwMTAwMDAwMFoYDzIwNTAwOTA0" + >+ "MDAwMDAwWjAsMSowKAYDVQQDDCFZdWJpY28gVTJGIEVFIFNlcmlhbCAyNDkxODIz" + >+ "MjQ3NzAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQ8yrksy5cofujmOUN+IfzW" + >+ "tvFlstWj89sTHTHBa3QrtHbY0emQgOtUbJu99VbmIQ/UJ4WJnnjMWJ6+MQ9s25/0" + >+ "ozswOTAiBgkrBgEEAYLECgIEFTEuMy42LjEuNC4xLjQxNDgyLjEuMjATBgsrBgEE" + >+ "AYLlHAIBAQQEAwIEMDANBgkqhkiG9w0BAQsFAAOCAQEAn5sFIki8TPQsxZkfyqus" + >+ "m2Ubvlvc3I7wrSwcH/s20YcV1C54skkiT5LH5uegXEnw5+TIgb8ulPReSiGDPXRW" + >+ "hR0PbBRaKVQMh08wksk0tD0iK4liwPQQzvHbdYkq8Ra0Spb101reo4IvxxRvYAQ4" + >+ "W8tptlyZ5+tpGXhnA8DYzUHo91zKRKqKtyWtjnmf86hpam8bJlbmMbHkAYPAj9pT" + >+ "+kqPhaBWk5RK4XmhM50ALRXKvYEAkOxyLvXe+ZZaNx1BXWJLaKJwfK2XvN0Xha+X" + >+ "4ljzPfVqAxqgNW2OjV68rcdOBxY2xrEQrOXMm5Df6srmQP8bsPH+XbTv96lfBgcz" + >+ "9TBFAiAyR3nGjzOAKIoRl7YJX3puubGxwSf2auEqmf6FMuwjuQIhAOOVFqxNYe5k" + >+ "BE1QtBWmpNTYS6bYlctat6GqfQgd40H6kAA="; >+const testU2fCredentialIdBase64 = >+ "Pr2Jv3fsUJdV7pwmNe-qrHsrnFzvFzbDcX2khTTIxrZU1_-UX1C1zE54BVvdOWtk" + >+ "942ixfliAMzUFc0I_kIAOA"; >+const testU2fSignResponse = >+ "AQAAADswRAIge94KUqwfTIsn4AOjcM1mpMcRjdItVEeDX0W5nGhCP/cCIDxRe0eH" + >+ "f4V4LeEAhqeD0effTjY553H19q+jWq1Tc4WOkAA="; > > const RESOURCES_DIR = "/WebKit/webauthn/resources/"; >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 191535
:
358560
|
358561
|
358578
|
358633
|
358644