[WebAuthn] LocalAuthenticator tests are failing on internal bots
authorjiewen_tan@apple.com <jiewen_tan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 23 Sep 2019 20:18:05 +0000 (20:18 +0000)
committerjiewen_tan@apple.com <jiewen_tan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 23 Sep 2019 20:18:05 +0000 (20:18 +0000)
https://bugs.webkit.org/show_bug.cgi?id=201844
<rdar://problem/54278693>

Reviewed by Brent Fulgham.

Source/WebKit:

This patch adds a way for mock tests to select a credential in getAssertion
ceremonies such that a test can ensure it always uses the credential it manages.
Credentials managed by other test could be deleted at anytime.

* UIProcess/API/C/WKWebsiteDataStoreRef.cpp:
(WKWebsiteDataStoreSetWebAuthenticationMockConfiguration):
* UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm:
(WebKit::LocalAuthenticator::getAssertion):
* UIProcess/WebAuthentication/Cocoa/LocalConnection.h:
* UIProcess/WebAuthentication/Cocoa/LocalConnection.mm:
(WebKit::LocalConnection::selectCredential const):
* UIProcess/WebAuthentication/Mock/MockLocalConnection.h:
* UIProcess/WebAuthentication/Mock/MockLocalConnection.mm:
(WebKit::MockLocalConnection::selectCredential const):
* UIProcess/WebAuthentication/Mock/MockWebAuthenticationConfiguration.h:

Tools:

LocalAuthenticator tests utilize Keychain for mock testing. Unlike iOS simulator tests which
each test runner is running in different simulator containers, all test runners are running
in the same macOS container in macOS. Therefore, Keychain is shared among all test runners
in macOS while it is not in iOS simulators. And therefore, race conditions would happen in
macOS which make the current tests flaky given they don't consider race conditions.

This patch then makes each test generate a random credential, and thus no other tests would
be able to access it, and therefore eliminate any race condition. To support this, a few new
functionalities are introduced to the mock test infrastructure as well:
1) TestRunner.cleanUpKeychain accepts a new parameter to more precisely identify an item.
2) WebAuthenticationMockConfiguration.Local has a new member to uniquely select a credential
for getAssertion ceremony when multiple presents.

* WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl:
* WebKitTestRunner/InjectedBundle/TestRunner.cpp:
(WTR::TestRunner::setWebAuthenticationMockConfiguration):
(WTR::TestRunner::cleanUpKeychain):
* WebKitTestRunner/InjectedBundle/TestRunner.h:
* WebKitTestRunner/TestController.h:
* WebKitTestRunner/TestInvocation.cpp:
(WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle):
* WebKitTestRunner/TestController.cpp:
(WTR::TestController::cleanUpKeychain):
* WebKitTestRunner/cocoa/TestControllerCocoa.mm:
(WTR::TestController::cleanUpKeychain):

LayoutTests:

This patch makes each test to generate their own credentials to avoid race conditions in Keychain
accesses when multiple test runners present.

* http/wpt/credential-management/credentialscontainer-store-basics.https.html:
* http/wpt/webauthn/idl.https.html:
These two tests are changed to use hid authenticator instead of local to avoid any future Keychain issues.
* http/wpt/webauthn/public-key-credential-create-failure-local-silent.https.html:
* http/wpt/webauthn/public-key-credential-create-failure-local.https.html:
* http/wpt/webauthn/public-key-credential-create-success-local.https.html:
* http/wpt/webauthn/public-key-credential-get-failure-local-silent.https.html:
* http/wpt/webauthn/public-key-credential-get-failure-local.https.html:
* http/wpt/webauthn/public-key-credential-get-success-local.https.html:
* http/wpt/webauthn/resources/util.js:
* platform/mac-wk2/TestExpectations:

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

27 files changed:
LayoutTests/ChangeLog
LayoutTests/http/wpt/credential-management/credentialscontainer-store-basics.https.html
LayoutTests/http/wpt/webauthn/idl.https.html
LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local-silent.https.html
LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local.https.html
LayoutTests/http/wpt/webauthn/public-key-credential-create-success-local.https.html
LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-local-silent.https.html
LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-local.https.html
LayoutTests/http/wpt/webauthn/public-key-credential-get-success-local.https.html
LayoutTests/http/wpt/webauthn/resources/util.js
LayoutTests/platform/mac-wk2/TestExpectations
Source/WebKit/ChangeLog
Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.cpp
Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm
Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalConnection.h
Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalConnection.mm
Source/WebKit/UIProcess/WebAuthentication/Mock/MockLocalConnection.h
Source/WebKit/UIProcess/WebAuthentication/Mock/MockLocalConnection.mm
Source/WebKit/UIProcess/WebAuthentication/Mock/MockWebAuthenticationConfiguration.h
Tools/ChangeLog
Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl
Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp
Tools/WebKitTestRunner/InjectedBundle/TestRunner.h
Tools/WebKitTestRunner/TestController.cpp
Tools/WebKitTestRunner/TestController.h
Tools/WebKitTestRunner/TestInvocation.cpp
Tools/WebKitTestRunner/cocoa/TestControllerCocoa.mm

index 3d01bfc..b56853c 100644 (file)
@@ -1,3 +1,26 @@
+2019-09-16  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthn] LocalAuthenticator tests are failing on internal bots
+        https://bugs.webkit.org/show_bug.cgi?id=201844
+        <rdar://problem/54278693>
+
+        Reviewed by Brent Fulgham.
+
+        This patch makes each test to generate their own credentials to avoid race conditions in Keychain
+        accesses when multiple test runners present.
+
+        * http/wpt/credential-management/credentialscontainer-store-basics.https.html:
+        * http/wpt/webauthn/idl.https.html:
+        These two tests are changed to use hid authenticator instead of local to avoid any future Keychain issues.
+        * http/wpt/webauthn/public-key-credential-create-failure-local-silent.https.html:
+        * http/wpt/webauthn/public-key-credential-create-failure-local.https.html:
+        * http/wpt/webauthn/public-key-credential-create-success-local.https.html:
+        * http/wpt/webauthn/public-key-credential-get-failure-local-silent.https.html:
+        * http/wpt/webauthn/public-key-credential-get-failure-local.https.html:
+        * http/wpt/webauthn/public-key-credential-get-success-local.https.html:
+        * http/wpt/webauthn/resources/util.js:
+        * platform/mac-wk2/TestExpectations:
+
 2019-09-23  Youenn Fablet  <youenn@apple.com>
 
         Simplify UserMediaPermissionRequestManager management of UserMediaRequest
index 10d8356..bc02119 100644 (file)
@@ -3,43 +3,28 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script>
-    const testES256PrivateKeyBase64 =
-        "BDj/zxSkzKgaBuS3cdWDF558of8AaIpgFpsjF/Qm1749VBJPgqUIwfhWHJ91nb7U" +
-        "PH76c0+WFOzZKslPyyFse4goGIW2R7k9VHLPEZl5nfnBgEVFh5zev+/xpHQIvuq6" +
-        "RQ==";
-    const testAttestationCertificateBase64 =
-        "MIIB6jCCAZCgAwIBAgIGAWHAxcjvMAoGCCqGSM49BAMCMFMxJzAlBgNVBAMMHkJh" +
-        "c2ljIEF0dGVzdGF0aW9uIFVzZXIgU3ViIENBMTETMBEGA1UECgwKQXBwbGUgSW5j" +
-        "LjETMBEGA1UECAwKQ2FsaWZvcm5pYTAeFw0xODAyMjMwMzM3MjJaFw0xODAyMjQw" +
-        "MzQ3MjJaMGoxIjAgBgNVBAMMGTAwMDA4MDEwLTAwMEE0OUEyMzBBMDIxM0ExGjAY" +
-        "BgNVBAsMEUJBQSBDZXJ0aWZpY2F0aW9uMRMwEQYDVQQKDApBcHBsZSBJbmMuMRMw" +
-        "EQYDVQQIDApDYWxpZm9ybmlhMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvCje" +
-        "Pzr6Sg76XMoHuGabPaG6zjpLFL8Zd8/74Hh5PcL2Zq+o+f7ENXX+7nEXXYt0S8Ux" +
-        "5TIRw4hgbfxXQbWLEqM5MDcwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBPAw" +
-        "FwYJKoZIhvdjZAgCBAowCKEGBAR0ZXN0MAoGCCqGSM49BAMCA0gAMEUCIAlK8A8I" +
-        "k43TbvKuYGHZs1DTgpTwmKTBvIUw5bwgZuYnAiEAtuJjDLKbGNJAJFMi5deEBqno" +
-        "pBTCqbfbDJccfyQpjnY=";
-    const testAttestationIssuingCACertificateBase64 =
-        "MIICIzCCAaigAwIBAgIIeNjhG9tnDGgwCgYIKoZIzj0EAwIwUzEnMCUGA1UEAwwe" +
-        "QmFzaWMgQXR0ZXN0YXRpb24gVXNlciBSb290IENBMRMwEQYDVQQKDApBcHBsZSBJ" +
-        "bmMuMRMwEQYDVQQIDApDYWxpZm9ybmlhMB4XDTE3MDQyMDAwNDIwMFoXDTMyMDMy" +
-        "MjAwMDAwMFowUzEnMCUGA1UEAwweQmFzaWMgQXR0ZXN0YXRpb24gVXNlciBTdWIg" +
-        "Q0ExMRMwEQYDVQQKDApBcHBsZSBJbmMuMRMwEQYDVQQIDApDYWxpZm9ybmlhMFkw" +
-        "EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEoSZ/1t9eBAEVp5a8PrXacmbGb8zNC1X3" +
-        "StLI9YO6Y0CL7blHmSGmjGWTwD4Q+i0J2BY3+bPHTGRyA9jGB3MSbaNmMGQwEgYD" +
-        "VR0TAQH/BAgwBgEB/wIBADAfBgNVHSMEGDAWgBSD5aMhnrB0w/lhkP2XTiMQdqSj" +
-        "8jAdBgNVHQ4EFgQU5mWf1DYLTXUdQ9xmOH/uqeNSD80wDgYDVR0PAQH/BAQDAgEG" +
-        "MAoGCCqGSM49BAMCA2kAMGYCMQC3M360LLtJS60Z9q3vVjJxMgMcFQ1roGTUcKqv" +
-        "W+4hJ4CeJjySXTgq6IEHn/yWab4CMQCm5NnK6SOSK+AqWum9lL87W3E6AA1f2TvJ" +
-        "/hgok/34jr93nhS87tOQNdxDS8zyiqw=";
-    const testRpId = "localhost";
-    function asciiToUint8Array(str)
-    {
-        var chars = [];
-        for (var i = 0; i < str.length; ++i)
-            chars.push(str.charCodeAt(i));
-        return new Uint8Array(chars);
-    }
+    const testCreationMessageBase64 =
+        "AKMBZnBhY2tlZAJYxEbMf7lnnVWy25CS4cjZ5eHQK3WA8LSBLHcJYuHkj1rYQQAA" +
+        "AE74oBHzjApNFYAGFxEfntx9AEAoCK3O6P5OyXN6V/f+9nAga0NA2Cgp4V3mgSJ5" +
+        "jOHLMDrmxp/S0rbD+aihru1C0aAN3BkiM6GNy5nSlDVqOgTgpQECAyYgASFYIEFb" +
+        "he3RkNud6sgyraBGjlh1pzTlCZehQlL/b18HZ6WGIlggJgfUd/en9p5AIqMQbUni" +
+        "nEeXdFLkvW0/zV5BpEjjNxADo2NhbGcmY3NpZ1hHMEUCIQDKg+ZBmEBtf0lWq4Re" +
+        "dH4/i/LOYqOR4uR2NAj2zQmw9QIgbTXb4hvFbj4T27bv/rGrc+y+0puoYOBkBk9P" +
+        "mCewWlNjeDVjgVkCwjCCAr4wggGmoAMCAQICBHSG/cIwDQYJKoZIhvcNAQELBQAw" +
+        "LjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEw" +
+        "IBcNMTQwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMG8xCzAJBgNVBAYTAlNF" +
+        "MRIwEAYDVQQKDAlZdWJpY28gQUIxIjAgBgNVBAsMGUF1dGhlbnRpY2F0b3IgQXR0" +
+        "ZXN0YXRpb24xKDAmBgNVBAMMH1l1YmljbyBVMkYgRUUgU2VyaWFsIDE5NTUwMDM4" +
+        "NDIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASVXfOt9yR9MXXv/ZzE8xpOh466" +
+        "4YEJVmFQ+ziLLl9lJ79XQJqlgaUNCsUvGERcChNUihNTyKTlmnBOUjvATevto2ww" +
+        "ajAiBgkrBgEEAYLECgIEFTEuMy42LjEuNC4xLjQxNDgyLjEuMTATBgsrBgEEAYLl" +
+        "HAIBAQQEAwIFIDAhBgsrBgEEAYLlHAEBBAQSBBD4oBHzjApNFYAGFxEfntx9MAwG" +
+        "A1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggEBADFcSIDmmlJ+OGaJvWn9Cqhv" +
+        "SeueToVFQVVvqtALOgCKHdwB+Wx29mg2GpHiMsgQp5xjB0ybbnpG6x212FxESJ+G" +
+        "inZD0ipchi7APwPlhIvjgH16zVX44a4e4hOsc6tLIOP71SaMsHuHgCcdH0vg5d2s" +
+        "c006WJe9TXO6fzV+ogjJnYpNKQLmCXoAXE3JBNwKGBIOCvfQDPyWmiiG5bGxYfPt" +
+        "y8Z3pnjX+1MDnM2hhr40ulMxlSNDnX/ZSnDyMGIbk8TOQmjTF02UO8auP8k3wt5D" +
+        "1rROIRU9+FCSX5WQYi68RuDrGMZB8P5+byoJqbKQdxn2LmE1oZAyohPAmLcoPO4=";
 
     promise_test(async function(t) {
         const options = {
                 },
                 user: {
                     name: "John Appleseed",
-                    id: asciiToUint8Array("123456"),
+                    id: new Uint8Array(16),
                     displayName: "Appleseed",
                 },
-                challenge: asciiToUint8Array("123456"),
-                pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+                challenge: new Uint8Array(16),
+                pubKeyCredParams: [{ type: "public-key", alg: -7 }]
             }
         };
         // A mock attestation object
         if (window.testRunner)
-            testRunner.setWebAuthenticationMockConfiguration({
-                local: {
-                    acceptAuthentication: true,
-                    acceptAttestation: true,
-                    privateKeyBase64: testES256PrivateKeyBase64,
-                    userCertificateBase64: testAttestationCertificateBase64,
-                    intermediateCACertificateBase64: testAttestationIssuingCACertificateBase64
-                }
-            });
+            testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", payloadBase64: [testCreationMessageBase64] } });
         const credential = await navigator.credentials.create(options);
 
-        return promise_rejects(t, "NotSupportedError",
-            navigator.credentials.store(credential)).then(() => {
-                if (window.testRunner)
-                    testRunner.cleanUpKeychain(testRpId);
-        });
+        return promise_rejects(t, "NotSupportedError", navigator.credentials.store(credential));
     }, "navigator.credentials.store().");
 </script>
index db63f6c..dc12b72 100644 (file)
 <div id='log'></div>
 
 <script>
-if (window.testRunner)
-    testRunner.setWebAuthenticationMockConfiguration({
-        local: {
-            acceptAuthentication: true,
-            acceptAttestation: true,
-            privateKeyBase64: testES256PrivateKeyBase64,
-            userCertificateBase64: testAttestationCertificateBase64,
-            intermediateCACertificateBase64: testAttestationIssuingCACertificateBase64
-        }
-    });
-
 promise_test(async () => {
     const idlURL = ["WebAuthN.idl"];
     const idlArray = new IdlArray();
@@ -54,7 +43,7 @@ promise_test(async () => {
                 name: "localhost",
             },
             user: {
-                name: "John Appleseed",
+                name: testUserhandleBase64,
                 id: Base64URL.parse(testUserhandleBase64),
                 displayName: "Appleseed",
             },
@@ -62,20 +51,21 @@ promise_test(async () => {
             pubKeyCredParams: [{ type: "public-key", alg: -7 }],
         }
     };
+    // A mock attestation object
+    if (window.testRunner)
+        testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", payloadBase64: [testCreationMessageBase64] } });
     createdCredential = await navigator.credentials.create(creationOptions);
 
-
     const requestOptions = {
         publicKey: {
             challenge: Base64URL.parse("MTIzNDU2"),
         }
     };
+    if (window.testRunner)
+        testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", payloadBase64: [testAssertionMessageBase64] } });
     requestedCredential = await navigator.credentials.get(requestOptions);
 
     idlArray.add_objects({"PublicKeyCredential": ["createdCredential"], "AuthenticatorAttestationResponse": ["createdCredential.response"], "AuthenticatorAssertionResponse": ["requestedCredential.response"]});
     idlArray.test();
-
-    if (window.testRunner)
-        testRunner.cleanUpKeychain(testRpId);
 }, "Setup for WebAuthN API IDL tests.");
 </script>
index 966c826..6a00bed 100644 (file)
 <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 } });
+    (async function() {
+        const userhandleBase64 = generateUserhandleBase64();
+        const privateKeyBase64 = await generatePrivateKeyBase64();
+        const credentialID = await calculateCredentialID(privateKeyBase64);
+        // 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: -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(() => {
+        promise_test(t => {
+            const options = {
+                publicKey: {
+                    rp: {
+                        name: "example.com"
+                    },
+                    user: {
+                        name: "John Appleseed",
+                        id: Base64URL.parse(userhandleBase64),
+                        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.cleanUpKeychain(testRpId);
-        });
-    }, "PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 2");
+                testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
+            return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.").then(() => {
+                if (window.testRunner)
+                    testRunner.cleanUpKeychain(testRpId, userhandleBase64);
+            });
+        }, "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(() => {
+        promise_test(t => {
+            const options = {
+                publicKey: {
+                    rp: {
+                        name: "example.com"
+                    },
+                    user: {
+                        name: "John Appleseed",
+                        id: Base64URL.parse(userhandleBase64),
+                        displayName: "John",
+                    },
+                    challenge: asciiToUint8Array("123456"),
+                    pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+                    excludeCredentials: [
+                        { type: "public-key", id: credentialID, transports: ["usb"] },
+                        { type: "public-key", id: credentialID, transports: ["nfc"] },
+                        { type: "public-key", id: credentialID, transports: ["ble"] },
+                        { type: "public-key", id: credentialID, transports: ["internal"] }
+                    ],
+                    timeout: 10
+                }
+            };
             if (window.testRunner)
-                testRunner.cleanUpKeychain(testRpId);
-        });
-    }, "PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 3");
+                testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
+            return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.").then(() => {
+                if (window.testRunner)
+                    testRunner.cleanUpKeychain(testRpId, userhandleBase64);
+            });
+        }, "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
+                }
+            };
+            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 } });
+            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
+        promise_test(t => {
+            const options = {
+                publicKey: {
+                    rp: {
+                        name: "example.com"
+                    },
+                    user: {
+                        name: "John Appleseed",
+                        id: Base64URL.parse(userhandleBase64),
+                        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(privateKeyBase64, testRpId, userhandleBase64);
             }
-        };
-        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");
+            return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.").then(() => {
+                if (window.testRunner)
+                    assert_false(testRunner.keyExistsInKeychain(testRpId, userhandleBase64));
+            });
+        }, "PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 6");
+    })();
 </script>
index 56fbc3e..c3c9372 100644 (file)
 <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({ local: { acceptAuthentication: false, acceptAttestation: false } });
+    (async function() {
+        const userhandleBase64 = generateUserhandleBase64();
+        const privateKeyBase64 = await generatePrivateKeyBase64();
+        const credentialID = await calculateCredentialID(privateKeyBase64);
+        // Default mock configuration. Tests need to override if they need different configuration.
+        if (window.testRunner)
+            testRunner.setWebAuthenticationMockConfiguration({ 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
-            }
-        };
-        return promiseRejects(t, "NotSupportedError", navigator.credentials.create(options), "The platform attached authenticator doesn't support any provided PublicKeyCredentialParameters.");
-    }, "PublicKeyCredential's [[create]] with unsupported public key credential parameters 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: -35 }, { type: "public-key", alg: -257 }], // ES384, RS256
+                }
+            };
+            return promiseRejects(t, "NotSupportedError", navigator.credentials.create(options), "The platform attached authenticator doesn't support any provided PublicKeyCredentialParameters.");
+        }, "PublicKeyCredential's [[create]] with unsupported public key credential parameters 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) }]
-            }
-        };
-        if (window.testRunner)
-            testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
-        return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "At least one credential matches an entry of the excludeCredentials list in the platform attached authenticator.").then(() => {
+        promise_test(t => {
+            const options = {
+                publicKey: {
+                    rp: {
+                        name: "example.com"
+                    },
+                    user: {
+                        name: "John Appleseed",
+                        id: Base64URL.parse(userhandleBase64),
+                        displayName: "John",
+                    },
+                    challenge: asciiToUint8Array("123456"),
+                    pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+                    excludeCredentials: [{ type: "public-key", id: credentialID }]
+                }
+            };
             if (window.testRunner)
-                testRunner.cleanUpKeychain(testRpId);
-        });
-    }, "PublicKeyCredential's [[create]] with matched exclude credentials in a mock local authenticator.");
+                testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
+            return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "At least one credential matches an entry of the excludeCredentials list in the platform attached authenticator.").then(() => {
+                if (window.testRunner)
+                    testRunner.cleanUpKeychain(testRpId, userhandleBase64);
+            });
+        }, "PublicKeyCredential's [[create]] with matched exclude credentials 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), 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"] }
-                ]
-            }
-        };
-        if (window.testRunner)
-            testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
-        return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "At least one credential matches an entry of the excludeCredentials list in the platform attached authenticator.").then(() => {
+        promise_test(t => {
+            const options = {
+                publicKey: {
+                    rp: {
+                        name: "example.com"
+                    },
+                    user: {
+                        name: "John Appleseed",
+                        id: Base64URL.parse(userhandleBase64),
+                        displayName: "John",
+                    },
+                    challenge: asciiToUint8Array("123456"),
+                    pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+                    excludeCredentials: [
+                        { type: "public-key", id: credentialID, transports: ["usb"] },
+                        { type: "public-key", id: credentialID, transports: ["nfc"] },
+                        { type: "public-key", id: credentialID, transports: ["ble"] },
+                        { type: "public-key", id: credentialID, transports: ["internal"] }
+                    ]
+                }
+            };
             if (window.testRunner)
-                testRunner.cleanUpKeychain(testRpId);
-        });
-    }, "PublicKeyCredential's [[create]] with matched exclude credentials in a mock local authenticator. 2nd");
-
-    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 }]
-            }
-        };
-        return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Couldn't get user consent.");
-    }, "PublicKeyCredential's [[create]] without user consent in a mock local authenticator.");
+                testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
+            return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "At least one credential matches an entry of the excludeCredentials list in the platform attached authenticator.").then(() => {
+                if (window.testRunner)
+                    testRunner.cleanUpKeychain(testRpId, userhandleBase64);
+            });
+        }, "PublicKeyCredential's [[create]] with matched exclude credentials in a mock local authenticator. 2nd");
 
-    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 }]
-            }
-        };
-        if (window.testRunner)
-            testRunner.setWebAuthenticationMockConfiguration({ local: { acceptAuthentication: true, acceptAttestation: false } });
-        return promiseRejects(t, "UnknownError", navigator.credentials.create(options), "Unknown internal error.");
-    }, "PublicKeyCredential's [[create]] without attestation 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 }]
+                }
+            };
+            return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Couldn't get user consent.");
+        }, "PublicKeyCredential's [[create]] without user consent 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 }]
-            }
-        };
-        if (window.testRunner) {
-            testRunner.setWebAuthenticationMockConfiguration({ local: { acceptAuthentication: true, acceptAttestation: false } });
-            testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
-        }
-        return promiseRejects(t, "UnknownError", navigator.credentials.create(options), "Unknown internal error.").then(() => {
+        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 }]
+                }
+            };
             if (window.testRunner)
-                assert_false(testRunner.keyExistsInKeychain(testRpId, testUserhandleBase64));
-        });
-    }, "PublicKeyCredential's [[create]] deleting old credential in a mock local authenticator.");
+                testRunner.setWebAuthenticationMockConfiguration({ local: { acceptAuthentication: true, acceptAttestation: false } });
+            return promiseRejects(t, "UnknownError", navigator.credentials.create(options), "Unknown internal error.");
+        }, "PublicKeyCredential's [[create]] without attestation 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" }
+        promise_test(t => {
+            const options = {
+                publicKey: {
+                    rp: {
+                        name: "example.com"
+                    },
+                    user: {
+                        name: userhandleBase64,
+                        id: Base64URL.parse(userhandleBase64),
+                        displayName: "John",
+                    },
+                    challenge: asciiToUint8Array("123456"),
+                    pubKeyCredParams: [{ type: "public-key", alg: -7 }]
+                }
+            };
+            if (window.testRunner) {
+                testRunner.setWebAuthenticationMockConfiguration({ local: { acceptAuthentication: true, acceptAttestation: false } });
+                testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
             }
-        };
+            return promiseRejects(t, "UnknownError", navigator.credentials.create(options), "Unknown internal error.").then(() => {
+                if (window.testRunner)
+                    assert_false(testRunner.keyExistsInKeychain(testRpId, userhandleBase64));
+            });
+        }, "PublicKeyCredential's [[create]] deleting old credential in a mock local authenticator.");
 
-        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.");
+        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 f1ba137..e51ba5f 100644 (file)
 <script src="./resources/util.js"></script>
 <script src="./resources/cbor.js"></script>
 <script>
-    // Default mock configuration. Tests need to override if they need different configuration.
-    if (window.testRunner)
-        testRunner.setWebAuthenticationMockConfiguration({
-            local: {
-                acceptAuthentication: true,
-                acceptAttestation: true,
-                privateKeyBase64: testES256PrivateKeyBase64,
-                userCertificateBase64: testAttestationCertificateBase64,
-                intermediateCACertificateBase64: testAttestationIssuingCACertificateBase64
+    (async function() {
+        const userhandleBase64 = generateUserhandleBase64();
+        const privateKeyBase64 = await generatePrivateKeyBase64();
+        const credentialID = await calculateCredentialID(privateKeyBase64);
+        // Default mock configuration. Tests need to override if they need different configuration.
+        if (window.testRunner)
+            testRunner.setWebAuthenticationMockConfiguration({
+                local: {
+                    acceptAuthentication: true,
+                    acceptAttestation: true,
+                    privateKeyBase64: privateKeyBase64,
+                    userCertificateBase64: testAttestationCertificateBase64,
+                    intermediateCACertificateBase64: testAttestationIssuingCACertificateBase64
+                }
+            });
+
+        function checkResult(credential, isNoneAttestation = true)
+        {
+            // Check keychain
+            if (window.testRunner) {
+                assert_true(testRunner.keyExistsInKeychain(testRpId, userhandleBase64));
+                testRunner.cleanUpKeychain(testRpId, userhandleBase64);
             }
-        });
-
-    function checkResult(credential, isNoneAttestation = true)
-    {
-        // Check keychain
-        if (window.testRunner) {
-            assert_true(testRunner.keyExistsInKeychain(testRpId, testUserhandleBase64));
-            testRunner.cleanUpKeychain(testRpId);
-        }
 
-        // Check respond
-        assert_array_equals(Base64URL.parse(credential.id), Base64URL.parse(testCredentialIdBase64));
-        assert_equals(credential.type, 'public-key');
-        assert_array_equals(new Uint8Array(credential.rawId), Base64URL.parse(testCredentialIdBase64));
-        assert_equals(bytesToASCIIString(credential.response.clientDataJSON), '{"type":"webauthn.create","challenge":"MTIzNDU2","origin":"https://localhost:9443"}');
-        assert_not_own_property(credential.getClientExtensionResults(), "appid");
-
-        // Check attestation
-        const attestationObject = CBOR.decode(credential.response.attestationObject);
-        if (isNoneAttestation)
-            assert_equals(attestationObject.fmt, "none");
-        else
-            assert_equals(attestationObject.fmt, "Apple");
-        // Check authData
-        const authData = decodeAuthData(attestationObject.authData);
-        assert_equals(bytesToHexString(authData.rpIdHash), "49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763");
-        assert_equals(authData.flags, 69);
-        assert_equals(authData.counter, 0);
-        assert_equals(bytesToHexString(authData.aaguid), "00000000000000000000000000000000");
-        assert_array_equals(authData.credentialID, Base64URL.parse(testCredentialIdBase64));
-        // Check self attestation
-        assert_true(checkPublicKey(authData.publicKey));
-        if (isNoneAttestation)
-            assert_object_equals(attestationObject.attStmt, { });
-        else {
-            assert_equals(attestationObject.attStmt.alg, -7);
-            assert_equals(attestationObject.attStmt.x5c.length, 2);
-            assert_array_equals(attestationObject.attStmt.x5c[0], Base64URL.parse(testAttestationCertificateBase64));
-            assert_array_equals(attestationObject.attStmt.x5c[1], Base64URL.parse(testAttestationIssuingCACertificateBase64));
-
-            // Check signature
-            let publicKeyData = new Uint8Array(65);
-            publicKeyData[0] = 0x04;
-            publicKeyData.set(authData.publicKey['-2'], 1);
-            publicKeyData.set(authData.publicKey['-3'], 33);
-            return crypto.subtle.importKey("raw", publicKeyData, {
-                name: "ECDSA",
-                namedCurve: "P-256"
-            }, false, ['verify']).then(publicKey => {
-                return crypto.subtle.verify({
+            // Check respond
+            assert_array_equals(Base64URL.parse(credential.id), credentialID);
+            assert_equals(credential.type, 'public-key');
+            assert_array_equals(new Uint8Array(credential.rawId), credentialID);
+            assert_equals(bytesToASCIIString(credential.response.clientDataJSON), '{"type":"webauthn.create","challenge":"MTIzNDU2","origin":"https://localhost:9443"}');
+            assert_not_own_property(credential.getClientExtensionResults(), "appid");
+
+            // Check attestation
+            const attestationObject = CBOR.decode(credential.response.attestationObject);
+            if (isNoneAttestation)
+                assert_equals(attestationObject.fmt, "none");
+            else
+                assert_equals(attestationObject.fmt, "Apple");
+            // Check authData
+            const authData = decodeAuthData(attestationObject.authData);
+            assert_equals(bytesToHexString(authData.rpIdHash), "49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763");
+            assert_equals(authData.flags, 69);
+            assert_equals(authData.counter, 0);
+            assert_equals(bytesToHexString(authData.aaguid), "00000000000000000000000000000000");
+            assert_array_equals(authData.credentialID, credentialID);
+            // Check self attestation
+            assert_true(checkPublicKey(authData.publicKey));
+            if (isNoneAttestation)
+                assert_object_equals(attestationObject.attStmt, { });
+            else {
+                assert_equals(attestationObject.attStmt.alg, -7);
+                assert_equals(attestationObject.attStmt.x5c.length, 2);
+                assert_array_equals(attestationObject.attStmt.x5c[0], Base64URL.parse(testAttestationCertificateBase64));
+                assert_array_equals(attestationObject.attStmt.x5c[1], Base64URL.parse(testAttestationIssuingCACertificateBase64));
+
+                // Check signature
+                let publicKeyData = new Uint8Array(65);
+                publicKeyData[0] = 0x04;
+                publicKeyData.set(authData.publicKey['-2'], 1);
+                publicKeyData.set(authData.publicKey['-3'], 33);
+                return crypto.subtle.importKey("raw", publicKeyData, {
                     name: "ECDSA",
-                    hash: "SHA-256"
-                }, publicKey, extractRawSignature(attestationObject.attStmt.sig), attestationObject.authData).then(verified => {
-                    assert_true(verified);
+                    namedCurve: "P-256"
+                }, false, ['verify']).then(publicKey => {
+                    return crypto.subtle.verify({
+                        name: "ECDSA",
+                        hash: "SHA-256"
+                    }, publicKey, extractRawSignature(attestationObject.attStmt.sig), attestationObject.authData).then(verified => {
+                        assert_true(verified);
+                    });
                 });
-            });
-        }
-    }
-
-    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 }],
-            }
-        };
-
-        return navigator.credentials.create(options).then(credential => {
-            checkResult(credential);
-        });
-    }, "PublicKeyCredential's [[create]] with minimum options in a mock local 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 }],
-                authenticatorSelection: { authenticatorAttachment: "platform" }
-            }
-        };
-
-        return navigator.credentials.create(options).then(credential => {
-            checkResult(credential);
-        });
-    }, "PublicKeyCredential's [[create]] with authenticatorSelection { 'platform' } 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), transports: ["usb"] },
-                    { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["nfc"] },
-                    { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["ble"] }
-                ]
-            }
-        };
-        if (window.testRunner)
-            testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
-
-        return navigator.credentials.create(options).then(credential => {
-            checkResult(credential);
-        });
-    }, "PublicKeyCredential's [[create]] with matched exclude credential ids but not transports in a mock local 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 }],
-                attestation: "none"
             }
-        };
-
-        return navigator.credentials.create(options).then(credential => {
-            checkResult(credential);
-        });
-    }, "PublicKeyCredential's [[create]] with none attestation in a mock local 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 }],
-                attestation: "indirect"
-            }
-        };
-
-        return navigator.credentials.create(options).then(credential => {
-            checkResult(credential, false);
-        });
-    }, "PublicKeyCredential's [[create]] with indirect attestation in a mock local 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 }],
-                attestation: "direct"
-            }
-        };
+        }
 
-        return navigator.credentials.create(options).then(credential => {
-            checkResult(credential, false);
-        });
-    }, "PublicKeyCredential's [[create]] with direct attestation in a mock local authenticator.");
+        promise_test(t => {
+            const options = {
+                publicKey: {
+                    rp: {
+                        name: "localhost",
+                    },
+                    user: {
+                        name: userhandleBase64,
+                        id: Base64URL.parse(userhandleBase64),
+                        displayName: "Appleseed",
+                    },
+                    challenge: Base64URL.parse("MTIzNDU2"),
+                    pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+                }
+            };
+
+            return navigator.credentials.create(options).then(credential => {
+                checkResult(credential);
+            });
+        }, "PublicKeyCredential's [[create]] with minimum options in a mock local authenticator.");
+
+        promise_test(t => {
+            const options = {
+                publicKey: {
+                    rp: {
+                        name: "localhost",
+                    },
+                    user: {
+                        name: userhandleBase64,
+                        id: Base64URL.parse(userhandleBase64),
+                        displayName: "Appleseed",
+                    },
+                    challenge: Base64URL.parse("MTIzNDU2"),
+                    pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+                    authenticatorSelection: { authenticatorAttachment: "platform" }
+                }
+            };
+
+            return navigator.credentials.create(options).then(credential => {
+                checkResult(credential);
+            });
+        }, "PublicKeyCredential's [[create]] with authenticatorSelection { 'platform' } in a mock local authenticator.");
+
+        promise_test(t => {
+            const options = {
+                publicKey: {
+                    rp: {
+                        name: "example.com"
+                    },
+                    user: {
+                        name: userhandleBase64,
+                        id: Base64URL.parse(userhandleBase64),
+                        displayName: "John",
+                    },
+                    challenge: asciiToUint8Array("123456"),
+                    pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+                    excludeCredentials: [
+                        { type: "public-key", id: credentialID, transports: ["usb"] },
+                        { type: "public-key", id: credentialID, transports: ["nfc"] },
+                        { type: "public-key", id: credentialID, transports: ["ble"] }
+                    ]
+                }
+            };
+            if (window.testRunner)
+                testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, userhandleBase64);
+
+            return navigator.credentials.create(options).then(credential => {
+                checkResult(credential);
+            });
+        }, "PublicKeyCredential's [[create]] with matched exclude credential ids but not transports in a mock local authenticator.");
+
+        promise_test(t => {
+            const options = {
+                publicKey: {
+                    rp: {
+                        name: "localhost",
+                    },
+                    user: {
+                        name: userhandleBase64,
+                        id: Base64URL.parse(userhandleBase64),
+                        displayName: "Appleseed",
+                    },
+                    challenge: Base64URL.parse("MTIzNDU2"),
+                    pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+                    attestation: "none"
+                }
+            };
+
+            return navigator.credentials.create(options).then(credential => {
+                checkResult(credential);
+            });
+        }, "PublicKeyCredential's [[create]] with none attestation in a mock local authenticator.");
+
+        promise_test(t => {
+            const options = {
+                publicKey: {
+                    rp: {
+                        name: "localhost",
+                    },
+                    user: {
+                        name: userhandleBase64,
+                        id: Base64URL.parse(userhandleBase64),
+                        displayName: "Appleseed",
+                    },
+                    challenge: Base64URL.parse("MTIzNDU2"),
+                    pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+                    attestation: "indirect"
+                }
+            };
+
+            return navigator.credentials.create(options).then(credential => {
+                checkResult(credential, false);
+            });
+        }, "PublicKeyCredential's [[create]] with indirect attestation in a mock local authenticator.");
+
+        promise_test(t => {
+            const options = {
+                publicKey: {
+                    rp: {
+                        name: "localhost",
+                    },
+                    user: {
+                        name: userhandleBase64,
+                        id: Base64URL.parse(userhandleBase64),
+                        displayName: "Appleseed",
+                    },
+                    challenge: Base64URL.parse("MTIzNDU2"),
+                    pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+                    attestation: "direct"
+                }
+            };
+
+            return navigator.credentials.create(options).then(credential => {
+                checkResult(credential, false);
+            });
+        }, "PublicKeyCredential's [[create]] with direct attestation in a mock local authenticator.");
+    })();
 </script>
index 0507d45..843ba86 100644 (file)
@@ -4,59 +4,64 @@
 <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 } });
+    (async function() {
+        const userhandleBase64 = generateUserhandleBase64();
+        const privateKeyBase64 = await generatePrivateKeyBase64();
+        const credentialID = await calculateCredentialID(privateKeyBase64);
+        // Default mock configuration. Tests need to override if they need different configuration.
+        if (window.testRunner)
+            testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, local: { acceptAuthentication: false, acceptAttestation: false, preferredUserhandleBase64: userhandleBase64 } });
 
-    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
-            }
-        };
+        promise_test(t => {
+            const options = {
+                publicKey: {
+                    challenge: asciiToUint8Array("123456"),
+                    allowCredentials: [
+                        { type: "public-key", id: credentialID, transports: ["usb"] },
+                        { type: "public-key", id: credentialID, transports: ["nfc"] },
+                        { type: "public-key", id: credentialID, transports: ["ble"] },
+                        { type: "public-key", id: credentialID, 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.");
+            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
-            }
-        };
+        promise_test(t => {
+            const options = {
+                publicKey: {
+                    challenge: asciiToUint8Array("123456"),
+                    allowCredentials: [
+                        { type: "public-key", id: Base64URL.parse(userhandleBase64) }
+                    ],
+                    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");
+            if (window.testRunner)
+                testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
+            return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Operation timed out.").then(() => {
+                    if (window.testRunner)
+                        testRunner.cleanUpKeychain(testRpId, userhandleBase64);
+                });
+        }, "PublicKeyCredential's [[get]] with silent failure in a mock local authenticator. 2");
 
-    promise_test(t => {
-        const options = {
-            publicKey: {
-                challenge: asciiToUint8Array("123456"),
-                timeout: 10
-            }
-        };
+        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");
+                testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
+            return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Operation timed out.").then(() => {
+                if (window.testRunner)
+                    testRunner.cleanUpKeychain(testRpId, userhandleBase64);
+            });
+        }, "PublicKeyCredential's [[get]] with silent failure in a mock local authenticator. 3");
+    })();
 </script>
index 29bb7f4..ea0e2a4 100644 (file)
@@ -4,72 +4,77 @@
 <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({ local: { acceptAuthentication: false, acceptAttestation: false } });
+    (async function() {
+        const userhandleBase64 = generateUserhandleBase64();
+        const privateKeyBase64 = await generatePrivateKeyBase64();
+        const credentialID = await calculateCredentialID(privateKeyBase64);
+        // Default mock configuration. Tests need to override if they need different configuration.
+        if (window.testRunner)
+            testRunner.setWebAuthenticationMockConfiguration({ local: { acceptAuthentication: false, acceptAttestation: false, preferredUserhandleBase64: userhandleBase64 } });
 
-    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"] }
-                ]
-            }
-        };
+        promise_test(t => {
+            const options = {
+                publicKey: {
+                    challenge: asciiToUint8Array("123456"),
+                    allowCredentials: [
+                        { type: "public-key", id: credentialID, transports: ["usb"] },
+                        { type: "public-key", id: credentialID, transports: ["nfc"] },
+                        { type: "public-key", id: credentialID, transports: ["ble"] },
+                        { type: "public-key", id: credentialID, transports: ["internal"] }
+                    ]
+                }
+            };
 
-        return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "No matched credentials are found in the platform attached authenticator.");
-    }, "PublicKeyCredential's [[get]] with no matched credentials in a mock local authenticator.");
+            return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "No matched credentials are found in the platform attached authenticator.");
+        }, "PublicKeyCredential's [[get]] with no matched credentials in a mock local authenticator.");
 
-    promise_test(t => {
-        const options = {
-            publicKey: {
-                challenge: asciiToUint8Array("123456"),
-                allowCredentials: [
-                    { type: "public-key", id: Base64URL.parse(testUserhandleBase64) }
-                ]
-            }
-        };
+        promise_test(t => {
+            const options = {
+                publicKey: {
+                    challenge: asciiToUint8Array("123456"),
+                    allowCredentials: [
+                        { type: "public-key", id: Base64URL.parse(userhandleBase64) }
+                    ]
+                }
+            };
 
-        if (window.testRunner)
-            testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
-        return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "No matched credentials are found in the platform attached authenticator.").then(() => {
+            if (window.testRunner)
+                testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
+            return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "No matched credentials are found in the platform attached authenticator.").then(() => {
                 if (window.testRunner)
-                    testRunner.cleanUpKeychain(testRpId);
+                    testRunner.cleanUpKeychain(testRpId, userhandleBase64);
             });
-    }, "PublicKeyCredential's [[get]] with no matched credentials in a mock local authenticator. 2nd");
+        }, "PublicKeyCredential's [[get]] with no matched credentials in a mock local authenticator. 2nd");
 
-    promise_test(t => {
-        const options = {
-            publicKey: {
-                challenge: asciiToUint8Array("123456")
-            }
-        };
+        promise_test(t => {
+            const options = {
+                publicKey: {
+                    challenge: asciiToUint8Array("123456")
+                }
+            };
 
-        if (window.testRunner)
-            testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
-        return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Couldn't get user consent.").then(() => {
             if (window.testRunner)
-                testRunner.cleanUpKeychain(testRpId);
-        });
-    }, "PublicKeyCredential's [[get]] without user consent in a mock local authenticator.");
+                testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
+            return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Couldn't get user consent.").then(() => {
+                if (window.testRunner)
+                    testRunner.cleanUpKeychain(testRpId, userhandleBase64);
+            });
+        }, "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
-            }
-        };
+        promise_test(t => {
+            const options = {
+                publicKey: {
+                    challenge: asciiToUint8Array("123456"),
+                    allowCredentials: [
+                        { type: "public-key", id: credentialID, transports: ["usb"] },
+                        { type: "public-key", id: credentialID, transports: ["nfc"] },
+                        { type: "public-key", id: credentialID, 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.");
+            return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Operation timed out.");
+        }, "PublicKeyCredential's [[get]] with timeout in a mock local authenticator.");
+    })();
 </script>
index 6badb53..e182094 100644 (file)
@@ -4,70 +4,75 @@
 <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({ local: { acceptAuthentication: true, acceptAttestation: false } });
-
-    function checkResult(credential)
-    {
+    (async function() {
+        const userhandleBase64 = generateUserhandleBase64();
+        const privateKeyBase64 = await generatePrivateKeyBase64();
+        const credentialID = await calculateCredentialID(privateKeyBase64);
+        // Default mock configuration. Tests need to override if they need different configuration.
         if (window.testRunner)
-            testRunner.cleanUpKeychain(testRpId);
+            testRunner.setWebAuthenticationMockConfiguration({ local: { acceptAuthentication: true, acceptAttestation: false, preferredUserhandleBase64: userhandleBase64 } });
+
+        function checkResult(credential)
+        {
+            if (window.testRunner)
+                testRunner.cleanUpKeychain(testRpId, userhandleBase64);
 
-         // Check respond
-        assert_array_equals(Base64URL.parse(credential.id), Base64URL.parse(testCredentialIdBase64));
-        assert_equals(credential.type, 'public-key');
-        assert_array_equals(new Uint8Array(credential.rawId), Base64URL.parse(testCredentialIdBase64));
-        assert_equals(bytesToASCIIString(credential.response.clientDataJSON), '{"type":"webauthn.get","challenge":"MTIzNDU2","origin":"https://localhost:9443"}');
-        assert_equals(bytesToHexString(credential.response.userHandle), "00010203040506070809");
-        assert_not_own_property(credential.getClientExtensionResults(), "appid");
+             // Check respond
+            assert_array_equals(Base64URL.parse(credential.id), credentialID);
+            assert_equals(credential.type, 'public-key');
+            assert_array_equals(new Uint8Array(credential.rawId), credentialID);
+            assert_equals(bytesToASCIIString(credential.response.clientDataJSON), '{"type":"webauthn.get","challenge":"MTIzNDU2","origin":"https://localhost:9443"}');
+            assert_array_equals(new Uint8Array(credential.response.userHandle), Base64URL.parse(userhandleBase64));
+            assert_not_own_property(credential.getClientExtensionResults(), "appid");
 
-        // Check authData
-        const authData = decodeAuthData(new Uint8Array(credential.response.authenticatorData));
-        assert_equals(bytesToHexString(authData.rpIdHash), "49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763");
-        assert_equals(authData.flags, 5);
-        assert_equals(authData.counter, 0);
+            // Check authData
+            const authData = decodeAuthData(new Uint8Array(credential.response.authenticatorData));
+            assert_equals(bytesToHexString(authData.rpIdHash), "49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763");
+            assert_equals(authData.flags, 5);
+            assert_equals(authData.counter, 0);
 
-        // Check signature
-        return crypto.subtle.importKey("raw", Base64URL.parse(testES256PublicKeyBase64), { name: "ECDSA", namedCurve: "P-256" }, false, ['verify']).then( publicKey => {
-            return crypto.subtle.digest("sha-256", credential.response.clientDataJSON).then ( hash => {
-                // credential.response.signature is in ASN.1 and WebCrypto expect signatures provides in r|s.
-                return crypto.subtle.verify({name: "ECDSA", hash: "SHA-256"}, publicKey, extractRawSignature(credential.response.signature), concatenateBuffers(credential.response.authenticatorData, hash)).then( verified => {
-                    assert_true(verified);
-                    assert_not_own_property(credential.getClientExtensionResults(), "appid");
+            // Check signature
+            return crypto.subtle.importKey("raw", Base64URL.parse(privateKeyBase64).slice(0, 65), { name: "ECDSA", namedCurve: "P-256" }, false, ['verify']).then( publicKey => {
+                return crypto.subtle.digest("sha-256", credential.response.clientDataJSON).then ( hash => {
+                    // credential.response.signature is in ASN.1 and WebCrypto expect signatures provides in r|s.
+                    return crypto.subtle.verify({name: "ECDSA", hash: "SHA-256"}, publicKey, extractRawSignature(credential.response.signature), concatenateBuffers(credential.response.authenticatorData, hash)).then( verified => {
+                        assert_true(verified);
+                        assert_not_own_property(credential.getClientExtensionResults(), "appid");
+                    });
                 });
             });
-        });
-    }
+        }
 
-    promise_test(t => {
-        const options = {
-            publicKey: {
-                challenge: Base64URL.parse("MTIzNDU2")
-            }
-        };
+        promise_test(t => {
+            const options = {
+                publicKey: {
+                    challenge: Base64URL.parse("MTIzNDU2")
+                }
+            };
 
-        if (window.testRunner)
-            testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
-        return navigator.credentials.get(options).then(credential => {
-            return checkResult(credential);
-        });
-    }, "PublicKeyCredential's [[get]] with minimum options in a mock local authenticator.");
+            if (window.testRunner)
+                testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
+            return navigator.credentials.get(options).then(credential => {
+                return checkResult(credential);
+            });
+        }, "PublicKeyCredential's [[get]] with minimum options in a mock local authenticator.");
 
-    promise_test(t => {
-        const options = {
-            publicKey: {
-                challenge: Base64URL.parse("MTIzNDU2"),
-                allowCredentials: [
-                    { type: "public-key", id: Base64URL.parse(testUserhandleBase64), transports: ["internal"] },
-                    { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["internal"] }
-                ]
-            }
-        };
+        promise_test(t => {
+            const options = {
+                publicKey: {
+                    challenge: Base64URL.parse("MTIzNDU2"),
+                    allowCredentials: [
+                        { type: "public-key", id: Base64URL.parse(userhandleBase64), transports: ["internal"] },
+                        { type: "public-key", id: credentialID, transports: ["internal"] }
+                    ]
+                }
+            };
 
-        if (window.testRunner)
-            testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
-        return navigator.credentials.get(options).then(credential => {
-            return checkResult(credential);
-        });
-    }, "PublicKeyCredential's [[get]] with matched allow credentials in a mock local authenticator.");
+            if (window.testRunner)
+                testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
+            return navigator.credentials.get(options).then(credential => {
+                return checkResult(credential);
+            });
+        }, "PublicKeyCredential's [[get]] with matched allow credentials in a mock local authenticator.");
+    })();
 </script>
index e300157..6efde8a 100644 (file)
@@ -3,9 +3,6 @@ const testES256PrivateKeyBase64 =
     "BDj/zxSkzKgaBuS3cdWDF558of8AaIpgFpsjF/Qm1749VBJPgqUIwfhWHJ91nb7U" +
     "PH76c0+WFOzZKslPyyFse4goGIW2R7k9VHLPEZl5nfnBgEVFh5zev+/xpHQIvuq6" +
     "RQ==";
-const testES256PublicKeyBase64 =
-    "BDj/zxSkzKgaBuS3cdWDF558of8AaIpgFpsjF/Qm1749VBJPgqUIwfhWHJ91nb7U" +
-    "PH76c0+WFOzZKslPyyFse4g=";
 const testRpId = "localhost";
 const testUserhandleBase64 = "AAECAwQFBgcICQ==";
 const testAttestationCertificateBase64 =
@@ -436,3 +433,43 @@ function checkU2fGetAssertionResult(credential, isAppID = false, appIDHash = "c2
     assert_equals(authData.flags, 1);
     assert_equals(authData.counter, 59);
 }
+
+function generateUserhandleBase64()
+{
+    let buffer = new Uint8Array(16);
+    crypto.getRandomValues(buffer);
+    return btoa(String.fromCharCode.apply(0, buffer));
+}
+
+async function generatePrivateKeyBase64()
+{
+    const algorithmKeyGen = {
+        name: "ECDSA",
+        namedCurve: "P-256"
+    };
+    const extractable = true;
+
+    const keyPair = await crypto.subtle.generateKey(algorithmKeyGen, extractable, ["sign", "verify"]);
+    const jwkPrivateKey = await crypto.subtle.exportKey("jwk", keyPair.privateKey);
+
+    const x = Base64URL.parse(jwkPrivateKey.x);
+    const y = Base64URL.parse(jwkPrivateKey.y);
+    const d = Base64URL.parse(jwkPrivateKey.d);
+
+    let buffer = new Uint8Array(x.length + y.length + d.length + 1);
+    buffer[0] = 0x04;
+    let pos = 1;
+    buffer.set(x, pos);
+    pos += x.length;
+    buffer.set(y, pos);
+    pos += y.length;
+    buffer.set(d, pos);
+
+    return btoa(String.fromCharCode.apply(0, buffer));
+}
+
+async function calculateCredentialID(privateKeyBase64) {
+    const privateKey = Base64URL.parse(privateKeyBase64);
+    const publicKey = privateKey.slice(0, 65);
+    return new Uint8Array(await crypto.subtle.digest("sha-1", publicKey));
+}
index aba46da..db1da1b 100644 (file)
@@ -890,8 +890,6 @@ http/wpt/webauthn/public-key-credential-create-success-local.https.html [ Skip ]
 http/wpt/webauthn/public-key-credential-get-failure-local.https.html [ Skip ]
 http/wpt/webauthn/public-key-credential-get-failure-local-silent.https.html [ Skip ]
 http/wpt/webauthn/public-key-credential-get-success-local.https.html [ Skip ]
-http/wpt/credential-management/credentialscontainer-store-basics.https.html [ Skip ]
-http/wpt/webauthn/idl.https.html [ Skip ]
 
 # NFC support require Catalina+, therefore skip them for HighSierra and Mojave.
 [ HighSierra Mojave ] http/wpt/webauthn/ctap-nfc-failure.https.html [ Skip ]
index 6a457a6..0fb72eb 100644 (file)
@@ -1,3 +1,27 @@
+2019-09-16  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthn] LocalAuthenticator tests are failing on internal bots
+        https://bugs.webkit.org/show_bug.cgi?id=201844
+        <rdar://problem/54278693>
+
+        Reviewed by Brent Fulgham.
+
+        This patch adds a way for mock tests to select a credential in getAssertion
+        ceremonies such that a test can ensure it always uses the credential it manages.
+        Credentials managed by other test could be deleted at anytime.
+
+        * UIProcess/API/C/WKWebsiteDataStoreRef.cpp:
+        (WKWebsiteDataStoreSetWebAuthenticationMockConfiguration):
+        * UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm:
+        (WebKit::LocalAuthenticator::getAssertion):
+        * UIProcess/WebAuthentication/Cocoa/LocalConnection.h:
+        * UIProcess/WebAuthentication/Cocoa/LocalConnection.mm:
+        (WebKit::LocalConnection::selectCredential const):
+        * UIProcess/WebAuthentication/Mock/MockLocalConnection.h:
+        * UIProcess/WebAuthentication/Mock/MockLocalConnection.mm:
+        (WebKit::MockLocalConnection::selectCredential const):
+        * UIProcess/WebAuthentication/Mock/MockWebAuthenticationConfiguration.h:
+
 2019-09-23  David Quesada  <david_quesada@apple.com>
 
         [iOS] REGRESSION(r250151): Occasional assertion failures in ShareableBitmap::~ShareableBitmap()
index 56b9973..58cd43f 100644 (file)
@@ -634,6 +634,8 @@ void WKWebsiteDataStoreSetWebAuthenticationMockConfiguration(WKWebsiteDataStoreR
             local.userCertificateBase64 = WebKit::toImpl(static_cast<WKStringRef>(WKDictionaryGetItemForKey(localRef, adoptWK(WKStringCreateWithUTF8CString("UserCertificateBase64")).get())))->string();
             local.intermediateCACertificateBase64 = WebKit::toImpl(static_cast<WKStringRef>(WKDictionaryGetItemForKey(localRef, adoptWK(WKStringCreateWithUTF8CString("IntermediateCACertificateBase64")).get())))->string();
         }
+        if (auto preferredUserhandleBase64Ref = static_cast<WKStringRef>(WKDictionaryGetItemForKey(localRef, adoptWK(WKStringCreateWithUTF8CString("PreferredUserhandleBase64")).get())))
+            local.preferredUserhandleBase64 = WebKit::toImpl(preferredUserhandleBase64Ref)->string();
         configuration.local = WTFMove(local);
     }
 
index fdb12c9..687d5da 100644 (file)
@@ -401,8 +401,7 @@ void LocalAuthenticator::getAssertion()
     }
 
     // Step 6.
-    // FIXME(rdar://problem/35900534): We don't have an UI to prompt users for selecting intersectedCredentials, and therefore we always use the first one for now.
-    NSDictionary *selectedCredentialAttributes = intersectedCredentialsAttributes[0];
+    auto *selectedCredentialAttributes = m_connection->selectCredential(intersectedCredentialsAttributes);
 
     // Step 7. Get user consent.
     // FIXME(rdar://problem/35900593): Update to a formal UI.
index cda2f73..cd785c2 100644 (file)
@@ -62,6 +62,7 @@ public:
     virtual void getUserConsent(const String& reason, UserConsentCallback&&) const;
     virtual void getUserConsent(const String& reason, SecAccessControlRef, UserConsentContextCallback&&) const;
     virtual void getAttestation(const String& rpId, const String& username, const Vector<uint8_t>& hash, AttestationCallback&&) const;
+    virtual NSDictionary *selectCredential(const NSArray *) const;
 };
 
 } // namespace WebKit
index 5c2c9a3..90a5f7b 100644 (file)
@@ -110,6 +110,12 @@ void LocalConnection::getAttestation(const String& rpId, const String& username,
 #endif // HAVE(DEVICE_IDENTITY)
 }
 
+NSDictionary *LocalConnection::selectCredential(const NSArray *credentials) const
+{
+    // FIXME(rdar://problem/35900534): We don't have an UI to prompt users for selecting intersectedCredentials, and therefore we always use the first one for now.
+    return credentials[0];
+}
+
 } // namespace WebKit
 
 #endif // ENABLE(WEB_AUTHN)
index 0d99786..31ad71a 100644 (file)
@@ -40,6 +40,7 @@ private:
     void getUserConsent(const String& reason, UserConsentCallback&&) const final;
     void getUserConsent(const String& reason, SecAccessControlRef, UserConsentContextCallback&&) const final;
     void getAttestation(const String& rpId, const String& username, const Vector<uint8_t>& hash, AttestationCallback&&) const final;
+    NSDictionary *selectCredential(const NSArray *) const final;
 
     MockWebAuthenticationConfiguration m_configuration;
 };
index 86e0790..571ecca 100644 (file)
@@ -108,6 +108,17 @@ void MockLocalConnection::getAttestation(const String& rpId, const String& usern
     });
 }
 
+NSDictionary *MockLocalConnection::selectCredential(const NSArray *credentials) const
+{
+    auto preferredUserhandle = adoptNS([[NSData alloc] initWithBase64EncodedString:m_configuration.local->preferredUserhandleBase64 options:0]);
+    for (NSDictionary *credential : credentials) {
+        if ([credential[(id)kSecAttrApplicationTag] isEqualToData:preferredUserhandle.get()])
+            return credential;
+    }
+    ASSERT_NOT_REACHED();
+    return nil;
+}
+
 } // namespace WebKit
 
 #endif // ENABLE(WEB_AUTHN)
index f40a7b7..913e85c 100644 (file)
@@ -38,6 +38,7 @@ struct MockWebAuthenticationConfiguration {
         String privateKeyBase64;
         String userCertificateBase64;
         String intermediateCACertificateBase64;
+        String preferredUserhandleBase64;
     };
 
     struct Hid {
index da911b9..cc4cb69 100644 (file)
@@ -1,3 +1,37 @@
+2019-09-16  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthn] LocalAuthenticator tests are failing on internal bots
+        https://bugs.webkit.org/show_bug.cgi?id=201844
+        <rdar://problem/54278693>
+
+        Reviewed by Brent Fulgham.
+
+        LocalAuthenticator tests utilize Keychain for mock testing. Unlike iOS simulator tests which
+        each test runner is running in different simulator containers, all test runners are running
+        in the same macOS container in macOS. Therefore, Keychain is shared among all test runners
+        in macOS while it is not in iOS simulators. And therefore, race conditions would happen in
+        macOS which make the current tests flaky given they don't consider race conditions.
+
+        This patch then makes each test generate a random credential, and thus no other tests would
+        be able to access it, and therefore eliminate any race condition. To support this, a few new
+        functionalities are introduced to the mock test infrastructure as well:
+        1) TestRunner.cleanUpKeychain accepts a new parameter to more precisely identify an item.
+        2) WebAuthenticationMockConfiguration.Local has a new member to uniquely select a credential
+        for getAssertion ceremony when multiple presents.
+
+        * WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl:
+        * WebKitTestRunner/InjectedBundle/TestRunner.cpp:
+        (WTR::TestRunner::setWebAuthenticationMockConfiguration):
+        (WTR::TestRunner::cleanUpKeychain):
+        * WebKitTestRunner/InjectedBundle/TestRunner.h:
+        * WebKitTestRunner/TestController.h:
+        * WebKitTestRunner/TestInvocation.cpp:
+        (WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle):
+        * WebKitTestRunner/TestController.cpp:
+        (WTR::TestController::cleanUpKeychain):
+        * WebKitTestRunner/cocoa/TestControllerCocoa.mm:
+        (WTR::TestController::cleanUpKeychain):
+
 2019-09-20  Aakash Jain  <aakash_jain@apple.com>
 
         [EWS] JSC queues should re-build ToT and compare results on build failure
index f534bb0..3cd94be 100644 (file)
@@ -391,7 +391,7 @@ interface TestRunner {
     // WebAuthN
     void setWebAuthenticationMockConfiguration(object configuration);
     void addTestKeyToKeychain(DOMString privateKeyBase64, DOMString attrLabel, DOMString applicationTagBase64);
-    void cleanUpKeychain(DOMString attrLabel);
+    void cleanUpKeychain(DOMString attrLabel, optional DOMString applicationTagBase64);
     boolean keyExistsInKeychain(DOMString attrLabel, DOMString applicationTagBase64);
 
     // Ad Click Attribution
index 1574ec4..b495cfa 100644 (file)
@@ -2579,7 +2579,7 @@ void TestRunner::setWebAuthenticationMockConfiguration(JSValueRef configurationV
             JSRetainPtr<JSStringRef> intermediateCACertificateBase64PropertyName(Adopt, JSStringCreateWithUTF8CString("intermediateCACertificateBase64"));
             JSValueRef intermediateCACertificateBase64Value = JSObjectGetProperty(context, local, intermediateCACertificateBase64PropertyName.get(), 0);
             if (!JSValueIsString(context, intermediateCACertificateBase64Value))
-            return;
+                return;
 
             localKeys.append(adoptWK(WKStringCreateWithUTF8CString("PrivateKeyBase64")));
             localValues.append(toWK(adopt(JSValueToStringCopy(context, privateKeyBase64Value, 0)).get()));
@@ -2589,6 +2589,16 @@ void TestRunner::setWebAuthenticationMockConfiguration(JSValueRef configurationV
             localValues.append(toWK(adopt(JSValueToStringCopy(context, intermediateCACertificateBase64Value, 0)).get()));
         }
 
+        JSRetainPtr<JSStringRef> preferredUserhandleBase64PropertyName(Adopt, JSStringCreateWithUTF8CString("preferredUserhandleBase64"));
+        JSValueRef preferredUserhandleBase64Value = JSObjectGetProperty(context, local, preferredUserhandleBase64PropertyName.get(), 0);
+        if (!JSValueIsUndefined(context, preferredUserhandleBase64Value) && !JSValueIsNull(context, preferredUserhandleBase64Value)) {
+            if (!JSValueIsString(context, preferredUserhandleBase64Value))
+                return;
+
+            localKeys.append(adoptWK(WKStringCreateWithUTF8CString("PreferredUserhandleBase64")));
+            localValues.append(toWK(adopt(JSValueToStringCopy(context, preferredUserhandleBase64Value, 0)).get()));
+        }
+
         Vector<WKStringRef> rawLocalKeys;
         Vector<WKTypeRef> rawLocalValues;
         rawLocalKeys.resize(localKeys.size());
@@ -2831,10 +2841,31 @@ void TestRunner::addTestKeyToKeychain(JSStringRef privateKeyBase64, JSStringRef
     WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), messageName.get(), messageBody.get(), nullptr);
 }
 
-void TestRunner::cleanUpKeychain(JSStringRef attrLabel)
+void TestRunner::cleanUpKeychain(JSStringRef attrLabel, JSStringRef applicationTagBase64)
 {
+    Vector<WKRetainPtr<WKStringRef>> keys;
+    Vector<WKRetainPtr<WKTypeRef>> values;
+
+    keys.append(adoptWK(WKStringCreateWithUTF8CString("AttrLabel")));
+    values.append(toWK(attrLabel));
+
+    if (applicationTagBase64) {
+        keys.append(adoptWK(WKStringCreateWithUTF8CString("ApplicationTag")));
+        values.append(toWK(applicationTagBase64));
+    }
+
+    Vector<WKStringRef> rawKeys;
+    Vector<WKTypeRef> rawValues;
+    rawKeys.resize(keys.size());
+    rawValues.resize(values.size());
+
+    for (size_t i = 0; i < keys.size(); ++i) {
+        rawKeys[i] = keys[i].get();
+        rawValues[i] = values[i].get();
+    }
+
     WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CleanUpKeychain"));
-    WKRetainPtr<WKStringRef> messageBody = adoptWK(WKStringCreateWithJSString(attrLabel));
+    WKRetainPtr<WKDictionaryRef> messageBody = adoptWK(WKDictionaryCreate(rawKeys.data(), rawValues.data(), rawKeys.size()));
 
     WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), messageName.get(), messageBody.get(), nullptr);
 }
index 939b6be..2da28bb 100644 (file)
@@ -488,7 +488,7 @@ public:
     void setWebAuthenticationMockConfiguration(JSValueRef);
     // FIXME(189876)
     void addTestKeyToKeychain(JSStringRef privateKeyBase64, JSStringRef attrLabel, JSStringRef applicationTagBase64);
-    void cleanUpKeychain(JSStringRef attrLabel);
+    void cleanUpKeychain(JSStringRef attrLabel, JSStringRef applicationTagBase64);
     bool keyExistsInKeychain(JSStringRef attrLabel, JSStringRef applicationTagBase64);
 
     void setCanHandleHTTPSServerTrustEvaluation(bool canHandle);
index 3a8c89a..d2a10ca 100644 (file)
@@ -3552,7 +3552,7 @@ void TestController::addTestKeyToKeychain(const String&, const String&, const St
 {
 }
 
-void TestController::cleanUpKeychain(const String&)
+void TestController::cleanUpKeychain(const String&, const String&)
 {
 }
 
index 837e4ab..af5e599 100644 (file)
@@ -292,7 +292,7 @@ public:
 
     void setWebAuthenticationMockConfiguration(WKDictionaryRef);
     void addTestKeyToKeychain(const String& privateKeyBase64, const String& attrLabel, const String& applicationTagBase64);
-    void cleanUpKeychain(const String& attrLabel);
+    void cleanUpKeychain(const String& attrLabel, const String& applicationTagBase64);
     bool keyExistsInKeychain(const String& attrLabel, const String& applicationTagBase64);
 
 #if PLATFORM(COCOA)
index 607e5bb..fd25200 100644 (file)
@@ -1586,8 +1586,16 @@ WKRetainPtr<WKTypeRef> TestInvocation::didReceiveSynchronousMessageFromInjectedB
     }
 
     if (WKStringIsEqualToUTF8CString(messageName, "CleanUpKeychain")) {
-        ASSERT(WKGetTypeID(messageBody) == WKStringGetTypeID());
-        TestController::singleton().cleanUpKeychain(toWTFString(static_cast<WKStringRef>(messageBody)));
+        ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
+        WKDictionaryRef testDictionary = static_cast<WKDictionaryRef>(messageBody);
+
+        WKRetainPtr<WKStringRef> attrLabelKey = adoptWK(WKStringCreateWithUTF8CString("AttrLabel"));
+        WKStringRef attrLabelWK = static_cast<WKStringRef>(WKDictionaryGetItemForKey(testDictionary, attrLabelKey.get()));
+
+        WKRetainPtr<WKStringRef> applicationTagKey = adoptWK(WKStringCreateWithUTF8CString("ApplicationTag"));
+        WKStringRef applicationTagWK = static_cast<WKStringRef>(WKDictionaryGetItemForKey(testDictionary, applicationTagKey.get()));
+
+        TestController::singleton().cleanUpKeychain(toWTFString(attrLabelWK), applicationTagWK ? toWTFString(applicationTagWK) : String());
         return nullptr;
     }
 
index 8863bdb..e4bc040 100644 (file)
@@ -363,18 +363,20 @@ void TestController::addTestKeyToKeychain(const String& privateKeyBase64, const
     ASSERT_UNUSED(status, !status);
 }
 
-void TestController::cleanUpKeychain(const String& attrLabel)
+void TestController::cleanUpKeychain(const String& attrLabel, const String& applicationTagBase64)
 {
-    NSDictionary* deleteQuery = @{
-        (id)kSecClass: (id)kSecClassKey,
-        (id)kSecAttrLabel: attrLabel,
+    auto deleteQuery = adoptNS([[NSMutableDictionary alloc] init]);
+    [deleteQuery setObject:(id)kSecClassKey forKey:(id)kSecClass];
+    [deleteQuery setObject:attrLabel forKey:(id)kSecAttrLabel];
 #if HAVE(DATA_PROTECTION_KEYCHAIN)
-        (id)kSecUseDataProtectionKeychain: @YES
+    [deleteQuery setObject:@YES forKey:(id)kSecUseDataProtectionKeychain];
 #else
-        (id)kSecAttrNoLegacy: @YES
+    [deleteQuery setObject:@YES forKey:(id)kSecAttrNoLegacy];
 #endif
-    };
-    SecItemDelete((__bridge CFDictionaryRef)deleteQuery);
+    if (!!applicationTagBase64)
+        [deleteQuery setObject:adoptNS([[NSData alloc] initWithBase64EncodedString:applicationTagBase64 options:NSDataBase64DecodingIgnoreUnknownCharacters]).get() forKey:(id)kSecAttrApplicationTag];
+
+    SecItemDelete((__bridge CFDictionaryRef)deleteQuery.get());
 }
 
 bool TestController::keyExistsInKeychain(const String& attrLabel, const String& applicationTagBase64)