[WebAuthN] Move time out control from WebProcess to UIProcess
authorjiewen_tan@apple.com <jiewen_tan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 4 Oct 2018 19:32:14 +0000 (19:32 +0000)
committerjiewen_tan@apple.com <jiewen_tan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 4 Oct 2018 19:32:14 +0000 (19:32 +0000)
https://bugs.webkit.org/show_bug.cgi?id=189642
<rdar://problem/44476765>

Reviewed by Chris Dumez.

Source/WebCore:

Since now the control unit of WebAuthN has been moved to UI Process, i.e. AuthenticatorManager,
the time out timer should move to UI Process as well.

Tests: http/wpt/webauthn/public-key-credential-create-failure-local-silent.https.html
       http/wpt/webauthn/public-key-credential-get-failure-local-silent.https.html

* Modules/webauthn/AuthenticatorCoordinator.cpp:
(WebCore::AuthenticatorCoordinator::create const):
(WebCore::AuthenticatorCoordinator::discoverFromExternalSource const):
(WebCore::AuthenticatorCoordinatorInternal::initTimeoutTimer): Deleted.
(WebCore::AuthenticatorCoordinatorInternal::didTimeoutTimerFire): Deleted.
* Modules/webauthn/PublicKeyCredentialCreationOptions.h:
(WebCore::PublicKeyCredentialCreationOptions::encode const):
(WebCore::PublicKeyCredentialCreationOptions::decode):
* Modules/webauthn/PublicKeyCredentialRequestOptions.h:
(WebCore::PublicKeyCredentialRequestOptions::encode const):
(WebCore::PublicKeyCredentialRequestOptions::decode):

Source/WebKit:

Besides adding a time out timer in the AuthenticatorManager, this patch also adds a new
option in MockWebAuthenticationConfiguration to turn on silent failure which is the
default policy of treating authenticators' error as suggested by spec.

* UIProcess/API/C/WKWebsiteDataStoreRef.cpp:
(WKWebsiteDataStoreSetWebAuthenticationMockConfiguration):
* UIProcess/WebAuthentication/AuthenticatorManager.cpp:
(WebKit::AuthenticatorManagerInternal::collectTransports):
(WebKit::AuthenticatorManager::makeCredential):
(WebKit::AuthenticatorManager::getAssertion):
(WebKit::AuthenticatorManager::respondReceived):
(WebKit::AuthenticatorManager::initTimeOutTimer):
* UIProcess/WebAuthentication/AuthenticatorManager.h:
(WebKit::AuthenticatorManager::requestTimeOutTimer):
* UIProcess/WebAuthentication/Mock/MockAuthenticatorManager.cpp:
(WebKit::MockAuthenticatorManager::respondReceivedInternal):
* UIProcess/WebAuthentication/Mock/MockWebAuthenticationConfiguration.h:

Tools:

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

LayoutTests:

This patch also fixes some flaky behaviours regarding to the dirty ASN.1 decoder.

* http/wpt/webauthn/public-key-credential-create-failure-local-silent.https-expected.txt: Added.
* http/wpt/webauthn/public-key-credential-create-failure-local-silent.https.html: Copied from LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local.https.html.
* http/wpt/webauthn/public-key-credential-create-failure-local.https-expected.txt:
* http/wpt/webauthn/public-key-credential-create-failure-local.https.html:
* http/wpt/webauthn/public-key-credential-create-failure.https-expected.txt:
* http/wpt/webauthn/public-key-credential-create-failure.https.html:
* http/wpt/webauthn/public-key-credential-get-failure-local-silent.https-expected.txt: Added.
* http/wpt/webauthn/public-key-credential-get-failure-local-silent.https.html: Copied from LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-local.https.html.
* http/wpt/webauthn/public-key-credential-get-failure-local.https-expected.txt:
* http/wpt/webauthn/public-key-credential-get-failure-local.https.html:
* http/wpt/webauthn/public-key-credential-get-failure.https-expected.txt:
* http/wpt/webauthn/public-key-credential-get-failure.https.html:
* http/wpt/webauthn/resources/util.js:

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

27 files changed:
LayoutTests/ChangeLog
LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local-silent.https-expected.txt [new file with mode: 0644]
LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local-silent.https.html [new file with mode: 0644]
LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local.https-expected.txt
LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local.https.html
LayoutTests/http/wpt/webauthn/public-key-credential-create-failure.https-expected.txt
LayoutTests/http/wpt/webauthn/public-key-credential-create-failure.https.html
LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-local-silent.https-expected.txt [new file with mode: 0644]
LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-local-silent.https.html [new file with mode: 0644]
LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-local.https-expected.txt
LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-local.https.html
LayoutTests/http/wpt/webauthn/public-key-credential-get-failure.https-expected.txt
LayoutTests/http/wpt/webauthn/public-key-credential-get-failure.https.html
LayoutTests/http/wpt/webauthn/resources/util.js
Source/WebCore/ChangeLog
Source/WebCore/Modules/webauthn/AuthenticatorCoordinator.cpp
Source/WebCore/Modules/webauthn/PublicKeyCredentialCreationOptions.h
Source/WebCore/Modules/webauthn/PublicKeyCredentialRequestOptions.h
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebKit/ChangeLog
Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.cpp
Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.cpp
Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.h
Source/WebKit/UIProcess/WebAuthentication/Mock/MockAuthenticatorManager.cpp
Source/WebKit/UIProcess/WebAuthentication/Mock/MockWebAuthenticationConfiguration.h
Tools/ChangeLog
Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp

index 9f38655..a6ef5fd 100644 (file)
@@ -1,3 +1,27 @@
+2018-10-04  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthN] Move time out control from WebProcess to UIProcess
+        https://bugs.webkit.org/show_bug.cgi?id=189642
+        <rdar://problem/44476765>
+
+        Reviewed by Chris Dumez.
+
+        This patch also fixes some flaky behaviours regarding to the dirty ASN.1 decoder.
+
+        * http/wpt/webauthn/public-key-credential-create-failure-local-silent.https-expected.txt: Added.
+        * http/wpt/webauthn/public-key-credential-create-failure-local-silent.https.html: Copied from LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local.https.html.
+        * http/wpt/webauthn/public-key-credential-create-failure-local.https-expected.txt:
+        * http/wpt/webauthn/public-key-credential-create-failure-local.https.html:
+        * http/wpt/webauthn/public-key-credential-create-failure.https-expected.txt:
+        * http/wpt/webauthn/public-key-credential-create-failure.https.html:
+        * http/wpt/webauthn/public-key-credential-get-failure-local-silent.https-expected.txt: Added.
+        * http/wpt/webauthn/public-key-credential-get-failure-local-silent.https.html: Copied from LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-local.https.html.
+        * http/wpt/webauthn/public-key-credential-get-failure-local.https-expected.txt:
+        * http/wpt/webauthn/public-key-credential-get-failure-local.https.html:
+        * http/wpt/webauthn/public-key-credential-get-failure.https-expected.txt:
+        * http/wpt/webauthn/public-key-credential-get-failure.https.html:
+        * http/wpt/webauthn/resources/util.js:
+
 2018-10-04  Chris Dumez  <cdumez@apple.com>
 
         Regression(r236779): Crash when changing the input element type from inside an 'input' event listener
diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local-silent.https-expected.txt b/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local-silent.https-expected.txt
new file mode 100644 (file)
index 0000000..667941f
--- /dev/null
@@ -0,0 +1,8 @@
+
+PASS PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 
+PASS PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 2 
+PASS PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 3 
+PASS PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 4 
+PASS PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 5 
+PASS PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 6 
+
diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local-silent.https.html b/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local-silent.https.html
new file mode 100644 (file)
index 0000000..966c826
--- /dev/null
@@ -0,0 +1,150 @@
+<!DOCTYPE html>
+<title>Web Authentication API: PublicKeyCredential's [[create]] silent failure cases with a mock local authenticator.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/util.js"></script>
+<script>
+    // Default mock configuration. Tests need to override if they need different configuration.
+    if (window.testRunner)
+        testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, local: { acceptAuthentication: false, acceptAttestation: false } });
+
+    promise_test(t => {
+        const options = {
+            publicKey: {
+                rp: {
+                    name: "example.com"
+                },
+                user: {
+                    name: "John Appleseed",
+                    id: Base64URL.parse(testUserhandleBase64),
+                    displayName: "John",
+                },
+                challenge: asciiToUint8Array("123456"),
+                pubKeyCredParams: [{ type: "public-key", alg: -35 }, { type: "public-key", alg: -257 }], // ES384, RS256
+                timeout: 10
+            }
+        };
+        return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.");
+    }, "PublicKeyCredential's [[create]] with silent failure in a mock local authenticator.");
+
+    promise_test(t => {
+        const options = {
+            publicKey: {
+                rp: {
+                    name: "example.com"
+                },
+                user: {
+                    name: "John Appleseed",
+                    id: Base64URL.parse(testUserhandleBase64),
+                    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.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
+        return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.").then(() => {
+            if (window.testRunner)
+                testRunner.cleanUpKeychain(testRpId);
+        });
+    }, "PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 2");
+
+    promise_test(t => {
+        const options = {
+            publicKey: {
+                rp: {
+                    name: "example.com"
+                },
+                user: {
+                    name: "John Appleseed",
+                    id: Base64URL.parse(testUserhandleBase64),
+                    displayName: "John",
+                },
+                challenge: asciiToUint8Array("123456"),
+                pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+                excludeCredentials: [
+                    { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["usb"] },
+                    { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["nfc"] },
+                    { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["ble"] },
+                    { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["internal"] }
+                ],
+                timeout: 10
+            }
+        };
+        if (window.testRunner)
+            testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
+        return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.").then(() => {
+            if (window.testRunner)
+                testRunner.cleanUpKeychain(testRpId);
+        });
+    }, "PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 3");
+
+    promise_test(t => {
+        const options = {
+            publicKey: {
+                rp: {
+                    name: "example.com"
+                },
+                user: {
+                    name: "John Appleseed",
+                    id: Base64URL.parse(testUserhandleBase64),
+                    displayName: "John",
+                },
+                challenge: asciiToUint8Array("123456"),
+                pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+                timeout: 10
+            }
+        };
+        return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.");
+    }, "PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 4");
+
+    promise_test(t => {
+        const options = {
+            publicKey: {
+                rp: {
+                    name: "example.com"
+                },
+                user: {
+                    name: "John Appleseed",
+                    id: Base64URL.parse(testUserhandleBase64),
+                    displayName: "John",
+                },
+                challenge: asciiToUint8Array("123456"),
+                pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+                timeout: 10
+            }
+        };
+        if (window.testRunner)
+            testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, local: { acceptAuthentication: true, acceptAttestation: false } });
+        return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.");
+    }, "PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 5");
+
+    promise_test(t => {
+        const options = {
+            publicKey: {
+                rp: {
+                    name: "example.com"
+                },
+                user: {
+                    name: "John Appleseed",
+                    id: Base64URL.parse(testUserhandleBase64),
+                    displayName: "John",
+                },
+                challenge: asciiToUint8Array("123456"),
+                pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+                timeout: 10
+            }
+        };
+        if (window.testRunner) {
+            testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, local: { acceptAuthentication: true, acceptAttestation: false } });
+            testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
+        }
+        return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.").then(() => {
+            if (window.testRunner)
+                assert_false(testRunner.keyExistsInKeychain(testRpId, testUserhandleBase64));
+        });
+    }, "PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 6");
+</script>
index a080f5f..fbe1a82 100644 (file)
@@ -5,4 +5,5 @@ PASS PublicKeyCredential's [[create]] with matched exclude credentials in a mock
 PASS PublicKeyCredential's [[create]] without user consent in a mock local authenticator. 
 PASS PublicKeyCredential's [[create]] without attestation in a mock local authenticator. 
 PASS PublicKeyCredential's [[create]] deleting old credential in a mock local authenticator. 
+PASS PublicKeyCredential's [[create]] with timeout in a mock local authenticator. 
 
index 3a6e49a..56fbc3e 100644 (file)
                 assert_false(testRunner.keyExistsInKeychain(testRpId, testUserhandleBase64));
         });
     }, "PublicKeyCredential's [[create]] deleting old credential in a mock local 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,
+                authenticatorSelection: { authenticatorAttachment: "cross-platform" }
+            }
+        };
+
+        if (window.testRunner)
+            testRunner.setWebAuthenticationMockConfiguration({ local: { acceptAuthentication: false, acceptAttestation: false } });
+        return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.");
+    }, "PublicKeyCredential's [[create]] with timeout in a mock local authenticator.");
 </script>
index 080d3ce..e2604ae 100644 (file)
@@ -1,4 +1,5 @@
 
+PASS PublicKeyCredential's [[create]] with timeout 
 PASS PublicKeyCredential's [[create]] with a mismatched RP ID 
 PASS PublicKeyCredential's [[create]] with an empty pubKeyCredParams 
 
index c52496c..9ea3428 100644 (file)
@@ -8,27 +8,26 @@
     if (window.testRunner)
         testRunner.setWebAuthenticationMockConfiguration({ });
 
-    // FIXME(189642): Re-enable the following test.
-    // 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: 0,
-    //         }
-    //     };
-    //
-    //     return promiseRejects(t, "NotAllowedError",
-    //         navigator.credentials.create(options), "Operation timed out.");
-    // }, "PublicKeyCredential's [[create]] with timeout");
+    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,
+            }
+        };
+
+        return promiseRejects(t, "NotAllowedError",
+            navigator.credentials.create(options), "Operation timed out.");
+    }, "PublicKeyCredential's [[create]] with timeout");
 
     promise_test(function(t) {
         const options = {
diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-local-silent.https-expected.txt b/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-local-silent.https-expected.txt
new file mode 100644 (file)
index 0000000..d7986c8
--- /dev/null
@@ -0,0 +1,5 @@
+
+PASS PublicKeyCredential's [[get]] with silent failure in a mock local authenticator. 
+PASS PublicKeyCredential's [[get]] with silent failure in a mock local authenticator. 2 
+PASS PublicKeyCredential's [[get]] with silent failure in a mock local authenticator. 3 
+
diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-local-silent.https.html b/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-local-silent.https.html
new file mode 100644 (file)
index 0000000..0507d45
--- /dev/null
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Web Authentication API: PublicKeyCredential's [[get]] failure cases.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/util.js"></script>
+<script>
+    // Default mock configuration. Tests need to override if they need different configuration.
+    if (window.testRunner)
+        testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, local: { acceptAuthentication: false, acceptAttestation: false } });
+
+    promise_test(t => {
+        const options = {
+            publicKey: {
+                challenge: asciiToUint8Array("123456"),
+                allowCredentials: [
+                    { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["usb"] },
+                    { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["nfc"] },
+                    { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["ble"] },
+                    { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["internal"] }
+                ],
+                timeout: 10
+            }
+        };
+
+        return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Operation timed out.");
+    }, "PublicKeyCredential's [[get]] with silent failure in a mock local authenticator.");
+
+    promise_test(t => {
+        const options = {
+            publicKey: {
+                challenge: asciiToUint8Array("123456"),
+                allowCredentials: [
+                    { type: "public-key", id: Base64URL.parse(testUserhandleBase64) }
+                ],
+                timeout: 10
+            }
+        };
+
+        if (window.testRunner)
+            testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
+        return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Operation timed out.").then(() => {
+                if (window.testRunner)
+                    testRunner.cleanUpKeychain(testRpId);
+            });
+    }, "PublicKeyCredential's [[get]] with silent failure in a mock local authenticator. 2");
+
+    promise_test(t => {
+        const options = {
+            publicKey: {
+                challenge: asciiToUint8Array("123456"),
+                timeout: 10
+            }
+        };
+
+        if (window.testRunner)
+            testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
+        return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Operation timed out.").then(() => {
+            if (window.testRunner)
+                testRunner.cleanUpKeychain(testRpId);
+        });
+    }, "PublicKeyCredential's [[get]] with silent failure in a mock local authenticator. 3");
+</script>
index d3f4f5d..878dcb7 100644 (file)
@@ -2,4 +2,5 @@
 PASS PublicKeyCredential's [[get]] with no matched credentials in a mock local authenticator. 
 PASS PublicKeyCredential's [[get]] with no matched credentials in a mock local authenticator. 2nd 
 PASS PublicKeyCredential's [[get]] without user consent in a mock local authenticator. 
+PASS PublicKeyCredential's [[get]] with timeout in a mock local authenticator. 
 
index f305a43..29bb7f4 100644 (file)
                 testRunner.cleanUpKeychain(testRpId);
         });
     }, "PublicKeyCredential's [[get]] without user consent in a mock local authenticator.");
+
+    promise_test(t => {
+        const options = {
+            publicKey: {
+                challenge: asciiToUint8Array("123456"),
+                allowCredentials: [
+                    { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["usb"] },
+                    { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["nfc"] },
+                    { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["ble"] }
+                ],
+                timeout: 10
+            }
+        };
+
+        return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Operation timed out.");
+    }, "PublicKeyCredential's [[get]] with timeout in a mock local authenticator.");
 </script>
index ce5e28b..b05eccb 100644 (file)
@@ -8,26 +8,26 @@
     if (window.testRunner)
         testRunner.setWebAuthenticationMockConfiguration({ });
 
-    // FIXME(189642): Re-enable the following test.
-    // promise_test(function(t) {
-    //     const options = {
-    //         publicKey: {
-    //             challenge: asciiToUint8Array("123456"),
-    //             timeout: 0,
-    //         }
-    //     };
-    //
-    //     return promiseRejects(t, "NotAllowedError",
-    //         navigator.credentials.get(options), "Operation timed out.");
-    // }, "PublicKeyCredential's [[get]] with timeout");
+    promise_test(t => {
+        const options = {
+            publicKey: {
+                challenge: asciiToUint8Array("123456"),
+                timeout: 10
+            }
+        };
+
+        return promiseRejects(t, "NotAllowedError",
+            navigator.credentials.get(options), "Operation timed out.");
+    }, "PublicKeyCredential's [[get]] with timeout");
 
-    promise_test(function(t) {
+    promise_test(t => {
         const options = {
             publicKey: {
                 rpId: "example.com",
-                challenge: asciiToUint8Array("123456"),
+                challenge: asciiToUint8Array("123456")
             }
         };
+
         return promiseRejects(t, "SecurityError",
             navigator.credentials.get(options), "The origin of the document is not a registrable domain suffix of the provided RP ID.");
     }, "PublicKeyCredential's [[get]] with a mismatched RP ID");
index 531f1ad..1bdcf83 100644 (file)
@@ -172,10 +172,19 @@ function extractRawSignature(asn1signature)
 {
     const signature = new Uint8Array(asn1signature);
     let tmp = new Uint8Array(64);
+
     const rStart =  signature[3] - 32;
-    tmp.set(new Uint8Array(signature.slice(4 + rStart, 36 + rStart)), 0);
+    if (rStart >= 0)
+        tmp.set(new Uint8Array(signature.slice(4 + rStart, 36 + rStart)), 0);
+    else
+        tmp.set(new Uint8Array(signature.slice(4, 36 + rStart)), -rStart);
+
     const sStart =  signature[37 + rStart] - 32;
-    tmp.set(new Uint8Array(signature.slice(38 + rStart + sStart)), 32);
+    if (sStart >= 0)
+        tmp.set(new Uint8Array(signature.slice(38 + rStart + sStart)), 32);
+    else
+        tmp.set(new Uint8Array(signature.slice(38 + rStart)), 32 - sStart);
+
     return tmp.buffer;
 }
 
index a0effdb..1fbc699 100644 (file)
@@ -1,3 +1,29 @@
+2018-10-04  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthN] Move time out control from WebProcess to UIProcess
+        https://bugs.webkit.org/show_bug.cgi?id=189642
+        <rdar://problem/44476765>
+
+        Reviewed by Chris Dumez.
+
+        Since now the control unit of WebAuthN has been moved to UI Process, i.e. AuthenticatorManager,
+        the time out timer should move to UI Process as well.
+
+        Tests: http/wpt/webauthn/public-key-credential-create-failure-local-silent.https.html
+               http/wpt/webauthn/public-key-credential-get-failure-local-silent.https.html
+
+        * Modules/webauthn/AuthenticatorCoordinator.cpp:
+        (WebCore::AuthenticatorCoordinator::create const):
+        (WebCore::AuthenticatorCoordinator::discoverFromExternalSource const):
+        (WebCore::AuthenticatorCoordinatorInternal::initTimeoutTimer): Deleted.
+        (WebCore::AuthenticatorCoordinatorInternal::didTimeoutTimerFire): Deleted.
+        * Modules/webauthn/PublicKeyCredentialCreationOptions.h:
+        (WebCore::PublicKeyCredentialCreationOptions::encode const):
+        (WebCore::PublicKeyCredentialCreationOptions::decode):
+        * Modules/webauthn/PublicKeyCredentialRequestOptions.h:
+        (WebCore::PublicKeyCredentialRequestOptions::encode const):
+        (WebCore::PublicKeyCredentialRequestOptions::decode):
+
 2018-10-04  Chris Dumez  <cdumez@apple.com>
 
         Regression(r236779): Crash when changing the input element type from inside an 'input' event listener
index cd4a51f..a0554b7 100644 (file)
@@ -38,7 +38,6 @@
 #include "PublicKeyCredentialData.h"
 #include "PublicKeyCredentialRequestOptions.h"
 #include "SecurityOrigin.h"
-#include "Timer.h"
 #include <pal/crypto/CryptoDigest.h>
 #include <wtf/JSONValues.h>
 #include <wtf/NeverDestroyed.h>
@@ -82,29 +81,6 @@ static Vector<uint8_t> produceClientDataJsonHash(const ArrayBuffer& clientDataJs
     return crypto->computeHash();
 }
 
-// FIXME(181947): We should probably trim timeOutInMs to some max allowable number.
-static std::unique_ptr<Timer> initTimeoutTimer(std::optional<unsigned long> timeOutInMs, const CredentialPromise& promise)
-{
-    if (!timeOutInMs)
-        return nullptr;
-
-    auto timer = std::make_unique<Timer>([promise = promise] () mutable {
-        promise.reject(Exception { NotAllowedError, "Operation timed out."_s });
-    });
-    timer->startOneShot(Seconds::fromMilliseconds(*timeOutInMs));
-    return timer;
-}
-
-static bool didTimeoutTimerFire(Timer* timer)
-{
-    if (!timer)
-        return false;
-    if (!timer->isActive())
-        return true;
-    timer->stop();
-    return false;
-}
-
 } // namespace AuthenticatorCoordinatorInternal
 
 AuthenticatorCoordinator::AuthenticatorCoordinator(std::unique_ptr<AuthenticatorCoordinatorClient>&& client)
@@ -130,9 +106,6 @@ void AuthenticatorCoordinator::create(const SecurityOrigin& callerOrigin, const
         return;
     }
 
-    // Step 4 & 17.
-    std::unique_ptr<Timer> timeoutTimer = initTimeoutTimer(options.timeout, promise);
-
     // Step 5-7.
     // FIXME(181950): We lack fundamental support from SecurityOrigin to determine if a host is a valid domain or not.
     // Step 6 is therefore skipped. Also, we lack the support to determine whether a domain is a registrable
@@ -156,7 +129,7 @@ void AuthenticatorCoordinator::create(const SecurityOrigin& callerOrigin, const
     auto clientDataJson = produceClientDataJson(ClientDataType::Create, options.challenge, callerOrigin);
     auto clientDataJsonHash = produceClientDataJsonHash(clientDataJson);
 
-    // Step 18-21.
+    // Step 4, 17-21.
     // Only platform attachments will be supported at this stage. Assuming one authenticator per device.
     // Also, resident keys, user verifications and direct attestation are enforced at this tage.
     // For better performance, transports of options.excludeCredentials are checked in LocalAuthenticator.
@@ -165,9 +138,7 @@ void AuthenticatorCoordinator::create(const SecurityOrigin& callerOrigin, const
         return;
     }
 
-    auto completionHandler = [clientDataJson = WTFMove(clientDataJson), promise = WTFMove(promise), timeoutTimer = WTFMove(timeoutTimer), abortSignal = WTFMove(abortSignal)] (const WebCore::PublicKeyCredentialData& data, const WebCore::ExceptionData& exception) mutable {
-        if (didTimeoutTimerFire(timeoutTimer.get()))
-            return;
+    auto completionHandler = [clientDataJson = WTFMove(clientDataJson), promise = WTFMove(promise), abortSignal = WTFMove(abortSignal)] (const WebCore::PublicKeyCredentialData& data, const WebCore::ExceptionData& exception) mutable {
         if (abortSignal && abortSignal->aborted()) {
             promise.reject(Exception { AbortError, "Aborted by AbortSignal."_s });
             return;
@@ -198,9 +169,6 @@ void AuthenticatorCoordinator::discoverFromExternalSource(const SecurityOrigin&
         return;
     }
 
-    // Step 4 & 16.
-    std::unique_ptr<Timer> timeoutTimer = initTimeoutTimer(options.timeout, promise);
-
     // Step 5-7.
     // FIXME(181950): We lack fundamental support from SecurityOrigin to determine if a host is a valid domain or not.
     // Step 6 is therefore skipped. Also, we lack the support to determine whether a domain is a registrable
@@ -216,7 +184,7 @@ void AuthenticatorCoordinator::discoverFromExternalSource(const SecurityOrigin&
     auto clientDataJson = produceClientDataJson(ClientDataType::Get, options.challenge, callerOrigin);
     auto clientDataJsonHash = produceClientDataJsonHash(clientDataJson);
 
-    // Step 14-15, 17-19.
+    // Step 4, 14-19.
     // Only platform attachments will be supported at this stage. Assuming one authenticator per device.
     // Also, resident keys, user verifications and direct attestation are enforced at this tage.
     // For better performance, filtering of options.allowCredentials is done in LocalAuthenticator.
@@ -225,9 +193,7 @@ void AuthenticatorCoordinator::discoverFromExternalSource(const SecurityOrigin&
         return;
     }
 
-    auto completionHandler = [clientDataJson = WTFMove(clientDataJson), promise = WTFMove(promise), timeoutTimer = WTFMove(timeoutTimer), abortSignal = WTFMove(abortSignal)] (const WebCore::PublicKeyCredentialData& data, const WebCore::ExceptionData& exception) mutable {
-        if (didTimeoutTimerFire(timeoutTimer.get()))
-            return;
+    auto completionHandler = [clientDataJson = WTFMove(clientDataJson), promise = WTFMove(promise), abortSignal = WTFMove(abortSignal)] (const WebCore::PublicKeyCredentialData& data, const WebCore::ExceptionData& exception) mutable {
         if (abortSignal && abortSignal->aborted()) {
             promise.reject(Exception { AbortError, "Aborted by AbortSignal."_s });
             return;
index bc17b58..6a43cca 100644 (file)
@@ -78,7 +78,7 @@ struct PublicKeyCredentialCreationOptions {
     BufferSource challenge;
     Vector<Parameters> pubKeyCredParams;
 
-    std::optional<unsigned long> timeout;
+    std::optional<unsigned> timeout;
     Vector<PublicKeyCredentialDescriptor> excludeCredentials;
     std::optional<AuthenticatorSelectionCriteria> authenticatorSelection;
 
@@ -125,7 +125,7 @@ void PublicKeyCredentialCreationOptions::encode(Encoder& encoder) const
     encoder << rp.id << rp.name << rp.icon;
     encoder << static_cast<uint64_t>(user.id.length());
     encoder.encodeFixedLengthData(user.id.data(), user.id.length(), 1);
-    encoder << user.displayName << user.name << user.icon << pubKeyCredParams << excludeCredentials << authenticatorSelection;
+    encoder << user.displayName << user.name << user.icon << pubKeyCredParams << timeout << excludeCredentials << authenticatorSelection;
 }
 
 template<class Decoder>
@@ -148,6 +148,13 @@ std::optional<PublicKeyCredentialCreationOptions> PublicKeyCredentialCreationOpt
         return std::nullopt;
     if (!decoder.decode(result.pubKeyCredParams))
         return std::nullopt;
+
+    std::optional<std::optional<unsigned>> timeout;
+    decoder >> timeout;
+    if (!timeout)
+        return std::nullopt;
+    result.timeout = WTFMove(*timeout);
+
     if (!decoder.decode(result.excludeCredentials))
         return std::nullopt;
 
@@ -155,7 +162,7 @@ std::optional<PublicKeyCredentialCreationOptions> PublicKeyCredentialCreationOpt
     decoder >> authenticatorSelection;
     if (!authenticatorSelection)
         return std::nullopt;
-    result.authenticatorSelection = WTFMove(authenticatorSelection.value());
+    result.authenticatorSelection = WTFMove(*authenticatorSelection);
 
     return result;
 }
index f013b2b..274476e 100644 (file)
@@ -35,7 +35,7 @@ namespace WebCore {
 
 struct PublicKeyCredentialRequestOptions {
     BufferSource challenge;
-    std::optional<unsigned long> timeout;
+    std::optional<unsigned> timeout;
     mutable String rpId;
     Vector<PublicKeyCredentialDescriptor> allowCredentials;
 
@@ -47,13 +47,20 @@ struct PublicKeyCredentialRequestOptions {
 template<class Encoder>
 void PublicKeyCredentialRequestOptions::encode(Encoder& encoder) const
 {
-    encoder << rpId << allowCredentials;
+    encoder << timeout << rpId << allowCredentials;
 }
 
 template<class Decoder>
 std::optional<PublicKeyCredentialRequestOptions> PublicKeyCredentialRequestOptions::decode(Decoder& decoder)
 {
     PublicKeyCredentialRequestOptions result;
+
+    std::optional<std::optional<unsigned>> timeout;
+    decoder >> timeout;
+    if (!timeout)
+        return std::nullopt;
+    result.timeout = WTFMove(*timeout);
+
     if (!decoder.decode(result.rpId))
         return std::nullopt;
     if (!decoder.decode(result.allowCredentials))
index f50ec65..d2171cc 100644 (file)
                        path = cbor;
                        sourceTree = "<group>";
                };
-               574F55DD204F3744002948C6 /* cocoa */ = {
-                       isa = PBXGroup;
-                       children = (
-                       );
-                       path = cocoa;
-                       sourceTree = "<group>";
-               };
                57C7A6881E56946D00C67D71 /* credentialmanagement */ = {
                        isa = PBXGroup;
                        children = (
                        isa = PBXGroup;
                        children = (
                                57303BB32006C6ED00355965 /* cbor */,
-                               574F55DD204F3744002948C6 /* cocoa */,
                                57303C272009B2FC00355965 /* AuthenticatorAssertionResponse.h */,
                                57303C292009B2FC00355965 /* AuthenticatorAssertionResponse.idl */,
                                57303C1B2009A98600355965 /* AuthenticatorAttestationResponse.h */,
index d5a12ba..aa659ca 100644 (file)
@@ -1,3 +1,29 @@
+2018-10-04  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthN] Move time out control from WebProcess to UIProcess
+        https://bugs.webkit.org/show_bug.cgi?id=189642
+        <rdar://problem/44476765>
+
+        Reviewed by Chris Dumez.
+
+        Besides adding a time out timer in the AuthenticatorManager, this patch also adds a new
+        option in MockWebAuthenticationConfiguration to turn on silent failure which is the
+        default policy of treating authenticators' error as suggested by spec.
+
+        * UIProcess/API/C/WKWebsiteDataStoreRef.cpp:
+        (WKWebsiteDataStoreSetWebAuthenticationMockConfiguration):
+        * UIProcess/WebAuthentication/AuthenticatorManager.cpp:
+        (WebKit::AuthenticatorManagerInternal::collectTransports):
+        (WebKit::AuthenticatorManager::makeCredential):
+        (WebKit::AuthenticatorManager::getAssertion):
+        (WebKit::AuthenticatorManager::respondReceived):
+        (WebKit::AuthenticatorManager::initTimeOutTimer):
+        * UIProcess/WebAuthentication/AuthenticatorManager.h:
+        (WebKit::AuthenticatorManager::requestTimeOutTimer):
+        * UIProcess/WebAuthentication/Mock/MockAuthenticatorManager.cpp:
+        (WebKit::MockAuthenticatorManager::respondReceivedInternal):
+        * UIProcess/WebAuthentication/Mock/MockWebAuthenticationConfiguration.h:
+
 2018-10-04  Yuhan Wu  <yuhan_wu@apple.com>
 
         runtime flag and IDL for MediaRecorder
index 67a5423..7b00416 100644 (file)
@@ -577,6 +577,10 @@ void WKWebsiteDataStoreSetWebAuthenticationMockConfiguration(WKWebsiteDataStoreR
 #if ENABLE(WEB_AUTHN)
     MockWebAuthenticationConfiguration configuration;
 
+    auto silentFailureRef = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(configurationRef, adoptWK(WKStringCreateWithUTF8CString("SilentFailure")).get()));
+    if (silentFailureRef)
+        configuration.silentFailure = WKBooleanGetValue(silentFailureRef);
+
     auto localRef = static_cast<WKDictionaryRef>(WKDictionaryGetItemForKey(configurationRef, adoptWK(WKStringCreateWithUTF8CString("Local")).get()));
     if (localRef) {
         MockWebAuthenticationConfiguration::Local local;
index 7355a32..ac9ba97 100644 (file)
@@ -38,6 +38,8 @@ using namespace WebCore;
 namespace AuthenticatorManagerInternal {
 
 const size_t maxTransportNumber = 1;
+// Suggested by WebAuthN spec as of 7 August 2018.
+const unsigned maxTimeOutValue = 120000;
 
 // FIXME(188623, 188624, 188625): Support USB, NFC and BLE authenticators.
 static AuthenticatorManager::TransportSet collectTransports(const std::optional<PublicKeyCredentialCreationOptions::AuthenticatorSelectionCriteria>& authenticatorSelection)
@@ -49,12 +51,12 @@ static AuthenticatorManager::TransportSet collectTransports(const std::optional<
         return result;
     }
 
-    if (authenticatorSelection.value().authenticatorAttachment == PublicKeyCredentialCreationOptions::AuthenticatorAttachment::Platform) {
+    if (authenticatorSelection->authenticatorAttachment == PublicKeyCredentialCreationOptions::AuthenticatorAttachment::Platform) {
         auto addResult = result.add(AuthenticatorTransport::Internal);
         ASSERT_UNUSED(addResult, addResult.isNewEntry);
         return result;
     }
-    if (authenticatorSelection.value().authenticatorAttachment == PublicKeyCredentialCreationOptions::AuthenticatorAttachment::CrossPlatform)
+    if (authenticatorSelection->authenticatorAttachment == PublicKeyCredentialCreationOptions::AuthenticatorAttachment::CrossPlatform)
         return result;
 
     ASSERT_NOT_REACHED();
@@ -104,6 +106,7 @@ void AuthenticatorManager::makeCredential(const Vector<uint8_t>& hash, const Pub
     // 1. Save request for async operations.
     m_pendingRequestData = { hash, true, options, { } };
     m_pendingCompletionHandler = WTFMove(callback);
+    initTimeOutTimer(options.timeout);
 
     // 2. Get available transports and start discovering authenticators on them.
     startDiscovery(collectTransports(options.authenticatorSelection));
@@ -121,6 +124,7 @@ void AuthenticatorManager::getAssertion(const Vector<uint8_t>& hash, const Publi
     // 1. Save request for async operations.
     m_pendingRequestData = { hash, false, { }, options };
     m_pendingCompletionHandler = WTFMove(callback);
+    initTimeOutTimer(options.timeout);
 
     // 2. Get available transports and start discovering authenticators on them.
     ASSERT(m_services.isEmpty());
@@ -147,11 +151,15 @@ void AuthenticatorManager::authenticatorAdded(Ref<Authenticator>&& authenticator
 void AuthenticatorManager::respondReceived(Respond&& respond)
 {
     ASSERT(RunLoop::isMain());
+    ASSERT(m_requestTimeOutTimer);
+    if (!m_requestTimeOutTimer->isActive())
+        return;
+
     ASSERT(m_pendingCompletionHandler);
-    // FIXME(189642)
     if (WTF::holds_alternative<PublicKeyCredentialData>(respond)) {
         m_pendingCompletionHandler(WTFMove(respond));
         clearState();
+        m_requestTimeOutTimer->stop();
         return;
     }
     respondReceivedInternal(WTFMove(respond));
@@ -178,6 +186,19 @@ void AuthenticatorManager::startDiscovery(const TransportSet& transports)
     }
 }
 
+void AuthenticatorManager::initTimeOutTimer(const std::optional<unsigned>& timeOutInMs)
+{
+    using namespace AuthenticatorManagerInternal;
+
+    unsigned timeOutInMsValue = std::min(maxTimeOutValue, timeOutInMs.value_or(maxTimeOutValue));
+
+    m_requestTimeOutTimer = std::make_unique<Timer>([context = this]() mutable {
+        context->m_pendingCompletionHandler((ExceptionData { NotAllowedError, "Operation timed out."_s }));
+        context->clearState();
+    });
+    m_requestTimeOutTimer->startOneShot(Seconds::fromMilliseconds(timeOutInMsValue));
+}
+
 } // namespace WebKit
 
 #endif // ENABLE(WEB_AUTHN)
index 503bc6b..ba22fd0 100644 (file)
@@ -32,6 +32,7 @@
 #include "WebAuthenticationRequestData.h"
 #include <WebCore/ExceptionData.h>
 #include <WebCore/PublicKeyCredentialData.h>
+#include <WebCore/Timer.h>
 #include <wtf/CompletionHandler.h>
 #include <wtf/HashSet.h>
 #include <wtf/Noncopyable.h>
@@ -57,6 +58,7 @@ public:
 
 protected:
     Callback& pendingCompletionHandler() { return m_pendingCompletionHandler; }
+    WebCore::Timer* requestTimeOutTimer() { return m_requestTimeOutTimer.get(); }
     void clearState();
 
 private:
@@ -72,10 +74,12 @@ private:
     virtual void respondReceivedInternal(Respond&&);
 
     void startDiscovery(const TransportSet&);
+    void initTimeOutTimer(const std::optional<unsigned>& timeOutInMs);
 
     // Request: We only allow one request per time.
     WebAuthenticationRequestData m_pendingRequestData;
     Callback m_pendingCompletionHandler;
+    std::unique_ptr<WebCore::Timer> m_requestTimeOutTimer;
 
     Vector<UniqueRef<AuthenticatorTransportService>> m_services;
     HashSet<Ref<Authenticator>> m_authenticators;
index 7e269b8..837aa26 100644 (file)
@@ -42,8 +42,12 @@ UniqueRef<AuthenticatorTransportService> MockAuthenticatorManager::createService
 
 void MockAuthenticatorManager::respondReceivedInternal(Respond&& respond)
 {
+    if (m_testConfiguration.silentFailure)
+        return;
+
     pendingCompletionHandler()(WTFMove(respond));
     clearState();
+    requestTimeOutTimer()->stop();
 }
 
 } // namespace WebKit
index 8afd8fe..639a12b 100644 (file)
@@ -38,6 +38,7 @@ struct MockWebAuthenticationConfiguration {
         String intermediateCACertificateBase64;
     };
 
+    bool silentFailure { false };
     std::optional<Local> local;
 };
 
index aabf6e9..48aa3a3 100644 (file)
@@ -1,3 +1,14 @@
+2018-10-04  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthN] Move time out control from WebProcess to UIProcess
+        https://bugs.webkit.org/show_bug.cgi?id=189642
+        <rdar://problem/44476765>
+
+        Reviewed by Chris Dumez.
+
+        * WebKitTestRunner/InjectedBundle/TestRunner.cpp:
+        (WTR::TestRunner::setWebAuthenticationMockConfiguration):
+
 2018-10-04  YUHAN WU  <yuhan_wu@apple.com>
 
         Runtime flag and IDL for MediaRecorder
index 5f2767a..25a0212 100644 (file)
@@ -2355,9 +2355,19 @@ void TestRunner::setWebAuthenticationMockConfiguration(JSValueRef configurationV
     Vector<WKRetainPtr<WKStringRef>> configurationKeys;
     Vector<WKRetainPtr<WKTypeRef>> configurationValues;
 
+    JSRetainPtr<JSStringRef> silentFailurePropertyName(Adopt, JSStringCreateWithUTF8CString("silentFailure"));
+    JSValueRef silentFailureValue = JSObjectGetProperty(context, configuration, silentFailurePropertyName.get(), 0);
+    if (!JSValueIsUndefined(context, silentFailureValue)) {
+        if (!JSValueIsBoolean(context, silentFailureValue))
+            return;
+        bool silentFailure = JSValueToBoolean(context, silentFailureValue);
+        configurationKeys.append({ AdoptWK, WKStringCreateWithUTF8CString("SilentFailure") });
+        configurationValues.append(adoptWK(WKBooleanCreate(silentFailure)).get());
+    }
+
     JSRetainPtr<JSStringRef> localPropertyName(Adopt, JSStringCreateWithUTF8CString("local"));
     JSValueRef localValue = JSObjectGetProperty(context, configuration, localPropertyName.get(), 0);
-    if (!JSValueIsNull(context, localValue)) {
+    if (!JSValueIsUndefined(context, localValue) && !JSValueIsNull(context, localValue)) {
         if (!JSValueIsObject(context, localValue))
             return;
         JSObjectRef local = JSValueToObject(context, localValue, 0);